בשיעור הזה תלמדו איך לשפר את הביצועים של האפליקציה הבאה על ידי הסרת יחסי תלות לא נחוצים או לא בשימוש.
מדידה
תמיד כדאי למדוד קודם את ביצועי האתר לפני שמוסיפים אופטימיזציות.
- כדי לראות תצוגה מקדימה של האתר, מקישים על View App ואז על Fullscreen .
קדימה, אפשר ללחוץ על החתלתול האהוב! באפליקציה הזו נעשה שימוש במסד הנתונים בזמן אמת של Firebase, ולכן הציון מתעדכן בזמן אמת ומסונכרן עם כל משתמש אחר באפליקציה. 🐈
- לוחצים על 'Control+Shift+J' (או 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח.
- לוחצים על הכרטיסייה רשתות.
- מסמנים את התיבה Disable cache (השבתת המטמון).
- טוענים מחדש את האפליקציה.
JavaScript בשווי של כמעט 1MB נשלח כדי לטעון את האפליקציה הפשוטה הזו!
כדאי לבדוק את האזהרות לגבי הפרויקט בכלי הפיתוח.
- לוחצים על הכרטיסייה מסוף.
- מוודאים שהשדה
Warnings
מופעל בתפריט הנפתח של הרמות, לצד הקלטFilter
.
- יש לקרוא את האזהרה המוצגת.
Firebase, שהיא אחת מהספריות שבהן נעשה שימוש באפליקציה הזו, מתפקדת כסאמריטן טובה כי היא שולחת אזהרה למפתחים כדי ליידע את המפתחים לא לייבא את החבילה המלאה, אלא רק את הרכיבים שנמצאים בשימוש. במילים אחרות, יש ספריות בשימוש שאפשר להסיר באפליקציה כדי שהטעינה שלה תהיה מהירה יותר.
יש גם מקרים שבהם משתמשים בספרייה מסוימת, אבל יכול להיות שיש חלופה פשוטה יותר. נבחן את הרעיון של הסרת ספריות לא נחוצות בהמשך המדריך.
ניתוח החבילה
יש שתי יחסי תלות עיקריים באפליקציה:
- Firebase: פלטפורמה שמספקת כמה שירותים שימושיים ל-iOS, ל-Android או לאפליקציות אינטרנט. כאן אפשר לאחסן ולסנכרן את המידע על כל חתלתול בזמן אמת באמצעות מסד הנתונים בזמן אמת.
- 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()
]
};
כשהאפליקציה נטענת מחדש, אתם אמורים לראות תצוגה חזותית של החבילה כולה במקום של האפליקציה עצמה.
לא חמוד כמו לראות חתלתולים Gboard, אבל בכל זאת עוזר להפליא. אם תעבירו את העכבר מעל כל אחת מהחבילות, תוכלו לראות שהגודל שלה מיוצג בשלוש דרכים שונות:
גודל הנתונים הסטטיסטיים | גודל לפני הקטנה או דחיסה. |
---|---|
גודל הניתוח | גודל החבילה בפועל בתוך החבילה לאחר ההידור. גרסה 4 של Webpack (שבשימוש באפליקציה הזו) מקטינה את הקבצים ההידור באופן אוטומטי, ולכן גודל זה קטן יותר מגודל הנתונים הסטטיסטיים. |
גודל קובץ ZIP | גודל החבילה לאחר הדחיסה באמצעות קידוד 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
בקלות, אבל אולי אפשר להסיר אותה לגמרי?
יום ההולדת של כל חתלתול חמוד נשמר בפורמט יוניקס (אלפיות שנייה) במסד הנתונים של Firebase.
זוהי חותמת הזמן של תאריך ושעה מסוימים, שמיוצגת על ידי מספר אלפיות השנייה שחלפו מאז 1 בינואר 1970 ב-00:00 לפי שעון UTC. אם אפשר לחשב את התאריך והשעה הנוכחיים באותו פורמט, אפשר לבנות פונקציה קטנה לאיתור הגיל של כל חתלתול בשבועות.
כמו תמיד, השתדלו לא להעתיק ולהדביק פריטים תוך כדי הקלדה. מתחילים על ידי הסרת moment
מהייבוא ב-src/index.js
.
import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';
יש פונקציות event 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
ב-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> `}) });
עכשיו צריך לטעון מחדש את האפליקציה ולבדוק את החלונית רשת שוב.
החבילה שלנו הצטמצמה שוב ביותר מחצי!
סיכום
בשיעור הזה תבינו היטב איך לנתח חבילה ספציפית ולמה כדאי להסיר חבילות שלא בשימוש או לא נחוצות. לפני שמתחילים לבצע אופטימיזציה לאפליקציה באמצעות השיטה הזו, חשוב לדעת שהתהליך עשוי להיות מורכב מאוד באפליקציות גדולות.
במקרה של הסרה של ספריות שלא בשימוש, כדאי לנסות לבדוק אילו חלקים של החבילה נמצאים בשימוש ואילו חלקים לא. כדי לקבל חבילה מסתורית שנראית כמו שלא משתמשים בה בשום מקום, קחו צעד אחורה ובדקו אילו יחסי תלות ברמה העליונה עשויים להזדקק לה. נסו למצוא דרך שאולי תוכלו להכפיל אותם זה מזה.
כשמדובר בהסרה של ספריות לא נחוצות, הדברים קצת יותר מורכבים. חשוב לעבוד בשיתוף פעולה הדוק עם הצוות ולבדוק אם יש פוטנציאל לפשט חלקים מ-codebase. יכול להיות ש תמיד יהיה צורך להסיר את moment
באפליקציה הזו, אבל מה היה קורה אם היו בו אזורי זמן ולוקאלים שונים שצריך לטפל בהם? מה יקרה אם היו שינויים מורכבים יותר בתאריכים? דברים יכולים להיות מסובכים מאוד כשמשנים ומנתחים תאריכים/שעות, וספריות כמו moment
ו-date-fns
מפשטות את זה משמעותית.
כל דבר הוא פתרון לא פשוט, וחשוב לבדוק אם שווה את המורכבות והמאמץ להשקת פתרון בהתאמה אישית במקום להסתמך על ספרייה של צד שלישי.