ใน Codelab นี้ คุณจะได้ปรับปรุงประสิทธิภาพของแอปพลิเคชันต่อไปนี้โดยนำทรัพยากร Dependency ที่ไม่ได้ใช้และไม่จำเป็นออก

วัดผล
คุณควรวัดประสิทธิภาพของเว็บไซต์ก่อนที่จะเพิ่มการเพิ่มประสิทธิภาพเสมอ
- หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกดเต็มหน้าจอ
คลิกที่ลูกแมวตัวโปรดได้เลย แอปพลิเคชันนี้ใช้ Realtime Database ของ Firebase ซึ่งเป็นสาเหตุที่คะแนนอัปเดตแบบเรียลไทม์และซิงค์กับทุกคนที่ใช้แอปพลิเคชัน 🐈
- กด `Control+Shift+J` (หรือ `Command+Option+J` บน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
- คลิกแท็บเครือข่าย
- เลือกช่องทำเครื่องหมายปิดใช้แคช
- โหลดแอปซ้ำ

ระบบกำลังส่ง JavaScript ขนาดเกือบ 1 MB เพื่อโหลดแอปพลิเคชันง่ายๆ นี้
ดูคำเตือนเกี่ยวกับโปรเจ็กต์ในเครื่องมือสำหรับนักพัฒนาเว็บ
- คลิกแท็บคอนโซล
- ตรวจสอบว่าได้เปิดใช้
Warningsในเมนูแบบเลื่อนลงของระดับข้างอินพุตFilter

- ดูคำเตือนที่แสดง

Firebase ซึ่งเป็นหนึ่งในไลบรารีที่ใช้ในแอปพลิเคชันนี้กำลังทำหน้าที่เป็นพลเมืองดีโดยการแสดงคำเตือนเพื่อให้นักพัฒนาแอปทราบว่าไม่ควรนำเข้าทั้งแพ็กเกจ แต่ควรนำเข้าเฉพาะคอมโพเนนต์ที่ใช้เท่านั้น กล่าวคือ มีไลบรารีที่ไม่ได้ใช้ซึ่งสามารถนำออกจากแอปพลิเคชันนี้เพื่อให้โหลดเร็วขึ้นได้
นอกจากนี้ยังมีบางกรณีที่มีการใช้ไลบรารีหนึ่งๆ แต่ก็อาจมีทางเลือกที่ง่ายกว่า เราจะพูดถึงแนวคิดในการนำไลบรารีที่ไม่จำเป็นออกในส่วนถัดไปของบทแนะนำนี้
กำลังวิเคราะห์แพ็กเกจ
แอปพลิเคชันมีการขึ้นต่อกันหลักๆ 2 รายการดังนี้
- Firebase: แพลตฟอร์มที่ให้บริการที่มีประโยชน์มากมายสำหรับแอปพลิเคชัน iOS, Android หรือเว็บแอปพลิเคชัน ในกรณีนี้ ระบบจะใช้ Realtime Database เพื่อจัดเก็บและซิงค์ข้อมูลของลูกแมวแต่ละตัวแบบเรียลไทม์
- Moment.js: ไลบรารียูทิลิตีที่ช่วยให้จัดการวันที่ใน JavaScript ได้ง่ายขึ้น ระบบจะจัดเก็บวันเกิดของลูกแมวแต่ละตัวไว้ในฐานข้อมูล Firebase และใช้
momentเพื่อคำนวณอายุเป็นสัปดาห์
ทรัพยากร Dependency เพียง 2 รายการจะทำให้ Bundle มีขนาดเกือบ 1 MB ได้อย่างไร เหตุผลหนึ่งก็คือทรัพยากร Dependency ใดๆ ก็ตามอาจมีทรัพยากร Dependency ของตัวเองได้ ดังนั้นจึงมีทรัพยากร 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 | ขนาดของแพ็กเกจหลังจากบีบอัดด้วยการเข้ารหัส gzip หัวข้อนี้จะกล่าวถึงในคู่มือแยกต่างหาก |
เครื่องมือ webpack-bundle-analyzer ช่วยให้ระบุแพ็กเกจที่ไม่ได้ใช้หรือไม่จำเป็นซึ่งคิดเป็นเปอร์เซ็นต์สูงของแพ็กเกจได้ง่ายขึ้น
กำลังนำแพ็กเกจที่ไม่ได้ใช้ออก
การแสดงภาพแสดงให้เห็นว่าแพ็กเกจ firebase ประกอบด้วยสิ่งต่างๆ มากกว่าแค่ฐานข้อมูล ซึ่งรวมถึงแพ็กเกจเพิ่มเติมต่อไปนี้
firestoreauthstoragemessagingfunctions
บริการเหล่านี้เป็นบริการที่ยอดเยี่ยมทั้งหมดที่ 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';
ตอนนี้เมื่อแอปโหลดซ้ำ คำเตือนเครื่องมือสำหรับนักพัฒนาเว็บจะไม่แสดง การเปิดแผงเครือข่ายของเครื่องมือสำหรับนักพัฒนาเว็บยังแสดงให้เห็นว่าขนาดแพ็กเกจลดลงอย่างมากด้วย

นำขนาดแพ็กเกจออกมากกว่าครึ่งหนึ่ง 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';
มี Firebase Listener เหตุการณ์ ที่จัดการการเปลี่ยนแปลงค่าในฐานข้อมูลของเรา ดังนี้
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 ซึ่งอยู่ในหน่วยมิลลิวินาทีอยู่แล้ว) แล้วหารด้วยจำนวนมิลลิวินาทีใน 1 สัปดาห์
สุดท้าย คุณสามารถนำอินสแตนซ์ทั้งหมดของ 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>
`})
});
ตอนนี้ให้โหลดแอปพลิเคชันซ้ำและดูแผงเครือข่ายอีกครั้ง

ขนาดของ Bundle ลดลงมากกว่าครึ่งอีกครั้ง
บทสรุป
เมื่อทำ Codelab นี้ คุณควรมีความเข้าใจที่เหมาะสมเกี่ยวกับวิธีวิเคราะห์แพ็กเกจหนึ่งๆ และเหตุผลที่การนำแพ็กเกจที่ไม่ได้ใช้หรือไม่จำเป็นออกจึงมีประโยชน์มาก ก่อนที่จะเริ่มเพิ่มประสิทธิภาพแอปพลิเคชันด้วยเทคนิคนี้ คุณควรทราบว่าวิธีนี้อาจซับซ้อนกว่ามากในแอปพลิเคชันขนาดใหญ่
ในส่วนของการนำไลบรารีที่ไม่ได้ใช้ออก ให้ลองดูว่ามีการใช้ส่วนใดของแพ็กเกจและส่วนใดที่ไม่ได้ใช้ สำหรับแพ็กเกจที่ดูลึกลับซึ่งดูเหมือนว่าจะไม่ได้ใช้ที่ใดเลย ให้ย้อนกลับไปดูว่าทรัพยากร Dependency ระดับบนสุดใดบ้างที่อาจต้องใช้แพ็กเกจดังกล่าว ลองหาวิธีแยกทั้ง 2 ส่วนออกจากกัน
เมื่อพูดถึงการนำไลบรารีที่ไม่จำเป็นออก สิ่งต่างๆ อาจซับซ้อนขึ้นเล็กน้อย สิ่งสำคัญคือต้องทำงานร่วมกับทีมอย่างใกล้ชิดและดูว่ามีโอกาสที่จะลดความซับซ้อนของบางส่วนในฐานของโค้ดหรือไม่ การนำ moment ออกในแอปพลิเคชันนี้อาจดูเหมือนเป็นสิ่งที่ควรทำทุกครั้ง แต่จะเกิดอะไรขึ้นหากมีเขตเวลาและภาษาที่แตกต่างกันซึ่งต้องจัดการ หรือจะเกิดอะไรขึ้นหากมีการจัดการวันที่ที่ซับซ้อนมากขึ้น การจัดการและการแยกวิเคราะห์วันที่/เวลาอาจเป็นเรื่องที่ยุ่งยากมาก และไลบรารีอย่าง moment และ date-fns จะช่วยลดความซับซ้อนนี้ได้อย่างมาก
ทุกอย่างมีข้อดีข้อเสีย และคุณควรประเมินว่าการเปิดตัวโซลูชันที่กำหนดเองแทนที่จะใช้ไลบรารีของบุคคลที่สามนั้นคุ้มค่ากับความซับซ้อนและความพยายามหรือไม่