ใน Codelab นี้ ให้ปรับปรุงประสิทธิภาพของแอปพลิเคชันต่อไปนี้ด้วยการนำทรัพยากร Dependency ที่ไม่ได้ใช้และไม่จำเป็นออก
วัดระยะทาง
คุณควรวัดว่าเว็บไซต์ทำงานได้ดีเพียงใดก่อนที่จะเพิ่มการเพิ่มประสิทธิภาพ
- หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกดเต็มหน้าจอ
ลองคลิกลูกแมวตัวโปรดของคุณดูเลย! มีการใช้ฐานข้อมูลเรียลไทม์ของ Firebase ในแอปพลิเคชันนี้ ซึ่งเป็นเหตุผลที่คะแนนอัปเดตแบบเรียลไทม์และซิงค์กับทุกๆ ผู้ใช้แอปพลิเคชัน 🐈
- กด "Control+Shift+J" (หรือ "Command+Option+J" ใน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
- คลิกแท็บเครือข่าย
- เลือกช่องทำเครื่องหมายปิดใช้แคช
- โหลดแอปซ้ำ
มีการส่ง JavaScript มูลค่าเกือบ 1 MB เพื่อโหลดแอปพลิเคชันง่ายๆ นี้
ดูคำเตือนของโปรเจ็กต์ในเครื่องมือสำหรับนักพัฒนาเว็บ
- คลิกแท็บคอนโซล
- ตรวจสอบว่าได้เปิดใช้
Warnings
ในเมนูแบบเลื่อนลงของระดับข้างอินพุตFilter
แล้ว
- ดูคำเตือนที่แสดง
Firebase ซึ่งเป็นหนึ่งในไลบรารีที่ใช้ในแอปพลิเคชันนี้ เป็นผู้สนับสนุนที่ดีด้วยการให้คำเตือนเพื่อแจ้งให้นักพัฒนาซอฟต์แวร์ทราบว่าอย่านำเข้าแพ็กเกจทั้งหมด แต่ให้เฉพาะคอมโพเนนต์ที่ใช้เท่านั้น กล่าวอีกนัยหนึ่งคือ คุณสามารถนำไลบรารีที่ไม่ได้ใช้ออกได้ในแอปพลิเคชันนี้เพื่อให้โหลดได้เร็วขึ้น
นอกจากนี้ยังมีกรณีที่มีการใช้ไลบรารีบางรายการ แต่อาจมีทางเลือกอื่นที่ง่ายกว่า เราจะสำรวจแนวคิดในการนำไลบรารีที่ไม่จำเป็นออกในช่วงท้ายของบทแนะนำนี้
กำลังวิเคราะห์แพ็กเกจ
แอปพลิเคชันมีทรัพยากร Dependency หลัก 2 รายการดังนี้
- Firebase: แพลตฟอร์มที่นำเสนอบริการที่มีประโยชน์มากมาย สำหรับ iOS, Android หรือเว็บแอปพลิเคชัน ฐานข้อมูลแบบเรียลไทม์ของทางบริษัทแห่งนี้ใช้เพื่อจัดเก็บและซิงค์ข้อมูลของลูกแมวแต่ละตัวแบบเรียลไทม์
- Moment.js: ไลบรารียูทิลิตีที่ช่วยให้จัดการวันที่ใน JavaScript ได้ง่ายขึ้น วันเกิดของลูกแมวแต่ละตัวจะอยู่ในฐานข้อมูล Firebase และใช้
moment
ในการคำนวณอายุของลูกแมวเป็นสัปดาห์
ทรัพยากร Dependency เพียง 2 รายการทำให้ Bundle มีขนาดเกือบ 1 MB ได้ได้อย่างไร เหตุผลข้อหนึ่งก็คือทรัพยากร Dependency ใดๆ ก็อาจมีการพึ่งพิงของตนเอง ดังนั้นจะมีมากกว่า 2 กรณีหากพิจารณาทุกความลึก/แขนงของ "ต้นไม้" แบบพึ่งพิง เป็นไปได้ง่ายที่แอปพลิเคชันจะมีขนาดใหญ่แบบสัมพัทธ์ อย่างรวดเร็วหากมีการรวมทรัพยากร Dependency ไว้หลายรายการ
วิเคราะห์ Bundler เพื่อให้เข้าใจถึงสิ่งที่จะเกิดขึ้นได้ดีขึ้น มีเครื่องมือมากมายที่ชุมชนสร้างขึ้น ซึ่งจะช่วยในการทำเช่นนี้ได้ เช่น
webpack-bundle-analyzer
มีแพ็กเกจสำหรับเครื่องมือนี้อยู่ในแอปแล้วในฐานะ devDependency
"devDependencies": {
//...
"webpack-bundle-analyzer": "^2.13.1"
},
ซึ่งหมายความว่าจะใช้ในไฟล์การกำหนดค่า Webpack ได้โดยตรง
นำเข้าที่ตอนต้นของ webpack.config.js
:
const path = require("path");
//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
คราวนี้ให้เพิ่มเป็นปลั๊กอินที่ท้ายไฟล์ภายในอาร์เรย์ plugins
module.exports = {
//...
plugins: [
//...
new BundleAnalyzerPlugin()
]
};
เมื่อแอปพลิเคชันโหลดซ้ำ คุณจะเห็นการแสดงภาพทั้งแพ็กเกจแทนที่จะเห็นตัวแอปเอง
ไม่น่ารักเท่าการเห็นลูกแมว ที่ปรากฏขึ้น ก็จริง แต่ก็ยังมีประโยชน์ได้อย่างน่าทึ่ง การวางเมาส์เหนือแพ็กเกจจะแสดงขนาดที่แสดงได้ใน 3 วิธีดังนี้
ขนาดสถิติ | ขนาดก่อนการลดขนาดหรือการบีบอัด |
---|---|
ขนาดที่แยกวิเคราะห์แล้ว | ขนาดของพัสดุจริงภายในแพ็กเกจหลังจากรวบรวมแล้ว Webpack เวอร์ชัน 4 (ซึ่งใช้ในแอปพลิเคชันนี้) จะลดขนาดไฟล์ที่คอมไพล์โดยอัตโนมัติ ทำให้มีขนาดเล็กกว่าขนาดสถิติ |
ขนาดที่บีบอัด | ขนาดของแพ็กเกจหลังจากบีบอัดด้วยการเข้ารหัส gzip หัวข้อนี้มีอยู่ในคู่มือแยกต่างหาก |
เครื่องมือวิเคราะห์แพ็กเกจ Webpack-bundle จะช่วยให้ระบุแพ็กเกจที่ไม่ได้ใช้หรือไม่จำเป็นซึ่งคิดเป็นสัดส่วนส่วนใหญ่ของแพ็กเกจได้ง่ายขึ้น
กำลังนำแพ็กเกจที่ไม่ได้ใช้ออก
การแสดงภาพแสดงให้เห็นว่าแพ็กเกจ firebase
ประกอบด้วยฐานข้อมูลมากกว่ามาก ซึ่งมีแพ็กเกจเพิ่มเติม เช่น
firestore
auth
storage
messaging
functions
นี่คือบริการชั้นยอดทั้งหมดที่ให้บริการโดย Firebase (และดูข้อมูลเพิ่มเติมในเอกสารประกอบ) แต่ไม่มีการใช้บริการเหล่านี้ในแอปพลิเคชัน เราจึงไม่มีเหตุผลที่จะต้องนำเข้าบริการทั้งหมด
เปลี่ยนกลับการเปลี่ยนแปลงใน webpack.config.js
เพื่อดูแอปพลิเคชันอีกครั้ง:
- นำ
BundleAnalyzerPlugin
ออกจากรายการปลั๊กอิน
plugins: [
//...
new BundleAnalyzerPlugin()
];
- และตอนนี้ให้นำการนำเข้าที่ไม่ได้ใช้ออกจากด้านบนของไฟล์ ดังนี้
const path = require("path");
//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
แอปพลิเคชันควรโหลดตามปกติแล้ว แก้ไข src/index.js
เพื่ออัปเดตการนำเข้า Firebase
import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';
ตอนนี้เมื่อแอปโหลดซ้ำ คำเตือนเครื่องมือสำหรับนักพัฒนาเว็บจะไม่แสดง การเปิดแผงเครือข่ายของเครื่องมือสำหรับนักพัฒนาเว็บยังช่วยลดขนาด Bundle ให้ลดลงได้ด้วย
ขนาดกลุ่มมีขนาดมากกว่าครึ่งหนึ่งถูกนำออก Firebase มีบริการต่างๆ มากมายและให้นักพัฒนาซอฟต์แวร์มีตัวเลือกในการรวมเฉพาะบริการที่จำเป็นจริงๆ ในแอปพลิเคชันนี้ จะใช้เพียง firebase/database
ในการจัดเก็บและซิงค์ข้อมูลทั้งหมด ต้องมีการนำเข้า firebase/app
ซึ่งจะตั้งค่าแพลตฟอร์ม API สำหรับแต่ละบริการเสมอ
ไลบรารียอดนิยมอื่นๆ อีกมากมาย เช่น lodash
ยังอนุญาตให้นักพัฒนาซอฟต์แวร์เลือกนำเข้าส่วนต่างๆ ของแพ็กเกจได้ด้วย โดยไม่ต้องทำอะไรมาก การอัปเดตไลบรารีจะนำเข้าในแอปพลิเคชันเพื่อรวมเฉพาะสิ่งที่ใช้งานอยู่ สามารถช่วยปรับปรุงประสิทธิภาพการทำงานได้อย่างมาก
แม้ว่าขนาดแพ็กเกจจะลดลงไปบ้างแล้ว แต่ยังคงมีงานที่ต้องทำมากกว่านี้ 😈
การนำแพ็กเกจที่ไม่จำเป็นออก
การนำเข้าส่วนต่างๆ ของไลบรารี moment
อาจทำได้ยากง่าย ซึ่งต่างจากที่ Firebase อาจนำออกทั้งหมดก็ได้
วันเกิดของลูกแมวน่ารักแต่ละตัวจะจัดเก็บไว้ในรูปแบบ Unix (มิลลิวินาที) ในฐานข้อมูล Firebase
นี่คือการประทับเวลาของวันที่และเวลาที่เจาะจงซึ่งแสดงเป็นมิลลิวินาทีที่ผ่านไปตั้งแต่วันที่ 1 มกราคม 1970 เวลา 00:00 น. (UTC) หากสามารถคำนวณวันที่และเวลาปัจจุบันในรูปแบบเดียวกันได้ อาจสร้างฟังก์ชันเล็กๆ ในการหาอายุของลูกแมวแต่ละตัวในแต่ละสัปดาห์
และเช่นเคย พยายามอย่าคัดลอกและวางขณะทําตามเนื้อหาที่นี่ เริ่มต้นด้วยการนำ moment
ออกจากการนําเข้าใน src/index.js
import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';
มี Listener เหตุการณ์ของ Firebase ที่จัดการการเปลี่ยนแปลงค่าในฐานข้อมูลของเรา ดังนี้
favoritesRef.on("value", (snapshot) => { ... })
ด้านบนนี้ ให้เพิ่มฟังก์ชันเล็กๆ ในการคำนวณจำนวนสัปดาห์นับจากวันที่ที่ระบุ ดังนี้
const ageInWeeks = birthDate => {
const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
const diff = Math.abs((new Date).getTime() - birthDate);
return Math.floor(diff / WEEK_IN_MILLISECONDS);
}
ในฟังก์ชันนี้ ระบบจะคำนวณความแตกต่างเป็นมิลลิวินาทีระหว่างวันที่และเวลาปัจจุบัน (new Date).getTime()
กับวันเกิด (อาร์กิวเมนต์ birthDate
อยู่แล้วเป็นมิลลิวินาที) และหารด้วยจำนวนมิลลิวินาทีในสัปดาห์เดียว
สุดท้าย คุณนำอินสแตนซ์ทั้งหมดของ moment
ออกจาก Listener เหตุการณ์ได้โดยใช้ฟังก์ชันนี้แทน
favoritesRef.on("value", (snapshot) => { const { kitties, favorites, names, birthDates } = snapshot.val(); favoritesScores = favorites; kittiesList.innerHTML = kitties.map((kittiePic, index) => {const birthday = moment(birthDates[index]);return ` <li> <img src=${kittiePic} onclick="favKittie(${index})"> <div class="extra"> <div class="details"> <p class="name">${names[index]}</p><p class="age">${moment().diff(birthday, 'weeks')} weeks old</p><p class="age">${ageInWeeks(birthDates[index])} weeks old</p> </div> <p class="score">${favorites[index]} ❤</p> </div> </li> `}) });
ตอนนี้ให้โหลดแอปพลิเคชันอีกครั้งแล้วดูที่แผงเครือข่ายอีกครั้ง
ขนาดแพ็กเกจของเราลดลงมากกว่าครึ่งหนึ่งอีกครั้ง!
บทสรุป
การใช้ Codelab นี้จะทำให้คุณเข้าใจวิธีวิเคราะห์แพ็กเกจหนึ่งๆ เป็นอย่างดีและเหตุผลที่การนำแพ็กเกจที่ไม่ได้ใช้หรือไม่จำเป็นออกจึงมีประโยชน์ ก่อนที่จะเริ่มเพิ่มประสิทธิภาพแอปพลิเคชันด้วยเทคนิคนี้ คุณต้องทราบว่าวิธีนี้อาจซับซ้อนกว่ามากในแอปพลิเคชันขนาดใหญ่
สำหรับการนำไลบรารีที่ไม่ได้ใช้ออก ให้ลองดูว่าส่วนใดของแพ็กเกจที่ใช้งานอยู่และส่วนใดที่ไม่ได้ใช้งาน สำหรับแพ็กเกจที่ดูลึกลับซึ่งดูเหมือนว่าจะไม่มีการใช้งานที่ใด ให้ย้อนกลับไปดูทรัพยากร Dependency ระดับบนสุดที่อาจจำเป็นต้องใช้ ลองหาวิธีที่จะแยก ความแตกต่างระหว่างกัน
เมื่อพูดถึงการนำไลบรารีที่ไม่จำเป็นออก ทุกอย่างอาจซับซ้อนขึ้นเล็กน้อย คุณต้องทำงานร่วมกับทีมอย่างใกล้ชิดและดูว่ามีโอกาสที่จะทำให้ส่วนต่างๆ ของฐานของโค้ดง่ายขึ้นหรือไม่ การนำ moment
ออกจากแอปพลิเคชันนี้อาจดูเหมือนเป็นสิ่งที่ควรทำทุกครั้ง แต่จะเกิดอะไรขึ้นหากมีเขตเวลาและภาษาต่างๆ ที่จำเป็นต้องจัดการ หรือถ้ามีการจัดการวันที่ที่ซับซ้อนขึ้น ควรทำอย่างไร สิ่งต่างๆ อาจซับซ้อนอย่างมากเมื่อปรับเปลี่ยนและแยกวิเคราะห์วันที่/เวลา และไลบรารีอย่าง moment
และ date-fns
ก็ลดความซับซ้อนลงอย่างมาก
ทุกสิ่งล้วนเป็นข้อดีข้อเสีย และสิ่งสำคัญคือต้องวัดว่าคุ้มค่ากับความซับซ้อนและความพยายามในการเปิดตัวโซลูชันที่กำหนดเองแทนการพึ่งพาไลบรารีของบุคคลที่สามหรือไม่