ב-codelab הזה, תשפרו את הביצועים של האפליקציה הבאה על ידי הסרת תלות לא בשימוש ולא נחוצה.
מדידה
תמיד כדאי קודם למדוד את הביצועים של אתר מסוים לפני שמוסיפים אופטימיזציות.
- כדי לראות תצוגה מקדימה של האתר, לוחצים על הצגת האפליקציה ואז על מסך מלא
.
אפשר ללחוץ על החתלתול האהוב עליכם. האפליקציה הזו משתמשת במסד נתונים בזמן אמת של Firebase, ולכן הניקוד מתעדכן בזמן אמת ומסונכרן עם כל משתמש אחר באפליקציה. 🐈
- מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את כלי הפיתוח.
- לוחצים על הכרטיסייה רשת.
- מסמנים את תיבת הסימון השבתת המטמון.
- טוענים מחדש את האפליקציה.
כמעט 1MB של JavaScript נשלח כדי לטעון את האפליקציה הפשוטה הזו!
כדאי לעיין באזהרות לגבי הפרויקט בכלי הפיתוח.
- לוחצים על הכרטיסייה Console.
- מוודאים שהאפשרות
Warnings
מופעלת בתפריט הנפתח של רמות הגישה לצד הקלטFilter
.
- בודקים את האזהרה שמוצגת.
Firebase, אחת מהספריות שנעשה בהן שימוש באפליקציה הזו, פועלת כ'שומרוני טוב' ומציגה אזהרה כדי להודיע למפתחים לא לייבא את כל החבילה שלה, אלא רק את הרכיבים שנמצאים בשימוש. במילים אחרות, יש ספריות שלא נעשה בהן שימוש שאפשר להסיר מהאפליקציה כדי שהיא תיטען מהר יותר.
יש גם מקרים שבהם נעשה שימוש בספרייה מסוימת, אבל יכול להיות שיש חלופה פשוטה יותר. בהמשך המדריך נסביר איך להסיר ספריות לא נחוצות.
ניתוח החבילה
יש שני יחסי תלות עיקריים באפליקציה:
- Firebase: פלטפורמה שמספקת מספר שירותים שימושיים לאפליקציות ל-iOS, ל-Android או לאינטרנט. כאן נעשה שימוש ב-Realtime Database כדי לאחסן ולסנכרן את המידע של כל גור חתולים בזמן אמת.
- Moment.js: ספריית כלי עזר שמקלה על הטיפול בתאריכים ב-JavaScript. תאריך הלידה של כל חתלתול מאוחסן במסד הנתונים של Firebase, והפונקציה
moment
משמשת לחישוב הגיל שלו בשבועות.
איך יכול להיות ששני יחסי תלות בלבד תורמים לגודל חבילה של כמעט 1MB? אחת הסיבות לכך היא שלכל תלות יכולות להיות תלויות משלה, ולכן יש הרבה יותר משתי תלויות אם לוקחים בחשבון כל עומק או ענף של 'עץ' התלויות. אם כוללים הרבה תלויות, קל מאוד שהאפליקציה תהפוך לגדולה יחסית במהירות.
כדאי לנתח את חבילת ה-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()
]
};
כשהאפליקציה נטענת מחדש, אמור להופיע ייצוג חזותי של כל החבילה במקום האפליקציה עצמה.
זה לא חמוד כמו לראות חתלתולים 🐱, אבל זה עדיין מאוד מועיל. כשמעבירים את העכבר מעל אחת מהחבילות, הגודל שלה מוצג בשלוש דרכים שונות:
גודל הנתונים הסטטיסטיים | הגודל לפני כל פעולת מזעור או דחיסה. |
---|---|
גודל מנותח | הגודל של החבילה בפועל בתוך ה-Bundle אחרי ההידור. גרסה 4 של webpack (שמשמשת באפליקציה הזו) מצמצמת את הקבצים המהודרים באופן אוטומטי, ולכן הגודל הזה קטן יותר מהגודל של הנתונים הסטטיסטיים. |
גודל אחרי דחיסה ב-gzip | גודל החבילה אחרי הדחיסה בקידוד gzip. הנושא הזה מוסבר במדריך נפרד. |
בעזרת הכלי webpack-bundle-analyzer, קל יותר לזהות חבילות לא בשימוש או לא נחוצות שמהוות אחוז גדול מהחבילה.
הסרת חבילות שלא בשימוש
הוויזואליזציה מראה שחבילת 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';
עכשיו, כשהאפליקציה נטענת מחדש, האזהרה בכלי הפיתוח לא מוצגת. כשפותחים את החלונית Network בכלי הפיתוח, אפשר לראות גם הפחתה משמעותית בגודל החבילה:
יותר ממחצית מגודל החבילה הוסר. Firebase מספקת שירותים רבים ושונים, ומאפשרת למפתחים לכלול רק את השירותים שהם צריכים בפועל. באפליקציה הזו, רק firebase/database
שימש לאחסון ולסנכרון של כל הנתונים. הייבוא firebase/app
, שקובע את פלטפורמת ה-API לכל אחד מהשירותים השונים, תמיד נדרש.
ספריות פופולריות רבות אחרות, כמו lodash
, מאפשרות גם למפתחים לייבא באופן סלקטיבי חלקים שונים מהחבילות שלהם. בלי להשקיע הרבה עבודה, אפשר לשפר משמעותית את הביצועים של אפליקציה על ידי עדכון של ייבוא הספריות כך שיכלול רק את מה שנמצא בשימוש.
למרות שהצלחנו להקטין את גודל החבילה באופן משמעותי, עדיין יש עוד עבודה לעשות. 😈
הסרת חבילות לא נחוצות
בניגוד ל-Firebase, אי אפשר לייבא חלקים מהספרייה moment
בקלות, אבל אולי אפשר להסיר אותה לגמרי?
תאריך הלידה של כל חתלתול חמוד מאוחסן בפורמט 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 שמטפל בשינויים בערכים במסד הנתונים שלנו:
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
ב-event 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> `}) });
עכשיו טוענים מחדש את האפליקציה ומסתכלים שוב על החלונית Network.
הגודל של החבילה שלנו קטן ביותר מחצי שוב!
סיכום
אחרי שתסיימו את ה-codelab הזה, תהיה לכם הבנה טובה לגבי ניתוח של חבילה מסוימת, ולגבי הסיבות לכך שכדאי להסיר חבילות שלא נמצאות בשימוש או שלא נחוצות. לפני שמתחילים בתהליך האופטימיזציה של אפליקציה באמצעות הטכניקה הזו, חשוב לדעת שהתהליך הזה יכול להיות מורכב הרבה יותר באפליקציות גדולות יותר.
בנוגע להסרת ספריות שלא נמצאות בשימוש, כדאי לנסות לברר אילו חלקים בחבילה נמצאים בשימוש ואילו לא. אם יש חבילה שנראית מוזרה ולא ברור למה היא משמשת, כדאי לבדוק אילו תלויות ברמה העליונה עשויות להזדקק לה. כדאי לנסות למצוא דרך להפריד ביניהם.
הסרת ספריות לא נחוצות יכולה להיות קצת יותר מסובכת. חשוב לעבוד בצמוד לצוות ולבדוק אם יש אפשרות לפשט חלקים מבסיס הקוד. יכול להיות שייראה לכם שהדבר הנכון לעשות בכל פעם הוא להסיר את moment
מהאפליקציה הזו, אבל מה אם יש אזורי זמן ולוקאלים שונים שצריך לטפל בהם? או מה אם יש מניפולציות מורכבות יותר של תאריכים? הטיפול בתאריכים ובשעות והניתוח שלהם יכול להיות מסובך מאוד, וספריות כמו moment
ו-date-fns
מפשטות את התהליך הזה באופן משמעותי.
כל דבר הוא פשרה, וחשוב להעריך אם כדאי בכלל להשקיע במורכבות ובמאמץ של פריסת פתרון בהתאמה אישית, במקום להסתמך על ספרייה של צד שלישי.