הסרת קוד שלא נמצא בשימוש

בשיעור הזה תלמדו איך לשפר את הביצועים של האפליקציה הבאה על ידי הסרת יחסי תלות לא נחוצים או לא בשימוש.

צילום מסך של האפליקציה

מדידה

תמיד כדאי למדוד קודם את ביצועי האתר לפני שמוסיפים אופטימיזציות.

  • כדי לראות תצוגה מקדימה של האתר, מקישים על View App ואז על Fullscreen מסך מלא.

קדימה, אפשר ללחוץ על החתלתול האהוב! באפליקציה הזו נעשה שימוש במסד הנתונים בזמן אמת של Firebase, ולכן הציון מתעדכן בזמן אמת ומסונכרן עם כל משתמש אחר באפליקציה. 🐈

  1. לוחצים על 'Control+Shift+J' (או 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח.
  2. לוחצים על הכרטיסייה רשתות.
  3. מסמנים את התיבה Disable cache (השבתת המטמון).
  4. טוענים מחדש את האפליקציה.

גודל החבילה המקורי: 992KB

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()
  ]
};

כשהאפליקציה נטענת מחדש, אתם אמורים לראות תצוגה חזותית של החבילה כולה במקום של האפליקציה עצמה.

כלי הניתוח של חבילות Webpack

לא חמוד כמו לראות חתלתולים 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 של כלי פיתוח מראה גם הפחתה יפה של גודל החבילה:

גודל החבילה הופחת ל-480KB

הוסר יותר ממחצית מגודל החבילה. פלטפורמת Firebase מספקת שירותים רבים ושונים, והמפתחים יכולים לכלול רק את אלה שבאמת יש להם צורך בה. באפליקציה הזו, נעשה שימוש רק ב-firebase/database כדי לאחסן ולסנכרן את כל הנתונים. הייבוא firebase/app, שמגדיר את פני ה-API לכל אחד מהשירותים השונים, נדרש תמיד.

ספריות פופולריות רבות אחרות, כמו lodash, מאפשרות גם למפתחים לייבא באופן סלקטיבי חלקים שונים מהחבילות שלהם. אם לא משקיעים הרבה עבודה, עדכון ייבוא של ספרייה באפליקציה כך שיכלול רק את מה שנמצא בשימוש יכול לשפר משמעותית את הביצועים.

למרות שגודל החבילה הצטמצם במידה מסוימת, עדיין יש עוד הרבה עבודה לעשות. 😈

מסירים חבילות שכבר לא נחוצות

שלא כמו Firebase, אי אפשר לייבא חלקים מספריית moment בקלות, אבל אולי אפשר להסיר אותה לגמרי?

יום ההולדת של כל חתלתול חמוד נשמר בפורמט יוניקס (אלפיות שנייה) במסד הנתונים של Firebase.

תאריכי לידה שמאוחסנים בפורמט Unix

זוהי חותמת הזמן של תאריך ושעה מסוימים, שמיוצגת על ידי מספר אלפיות השנייה שחלפו מאז 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>
    `})
});

עכשיו צריך לטעון מחדש את האפליקציה ולבדוק את החלונית רשת שוב.

גודל החבילה הופחת ל-225KB

החבילה שלנו הצטמצמה שוב ביותר מחצי!

סיכום

בשיעור הזה תבינו היטב איך לנתח חבילה ספציפית ולמה כדאי להסיר חבילות שלא בשימוש או לא נחוצות. לפני שמתחילים לבצע אופטימיזציה לאפליקציה באמצעות השיטה הזו, חשוב לדעת שהתהליך עשוי להיות מורכב מאוד באפליקציות גדולות.

במקרה של הסרה של ספריות שלא בשימוש, כדאי לנסות לבדוק אילו חלקים של החבילה נמצאים בשימוש ואילו חלקים לא. כדי לקבל חבילה מסתורית שנראית כמו שלא משתמשים בה בשום מקום, קחו צעד אחורה ובדקו אילו יחסי תלות ברמה העליונה עשויים להזדקק לה. נסו למצוא דרך שאולי תוכלו להכפיל אותם זה מזה.

כשמדובר בהסרה של ספריות לא נחוצות, הדברים קצת יותר מורכבים. חשוב לעבוד בשיתוף פעולה הדוק עם הצוות ולבדוק אם יש פוטנציאל לפשט חלקים מ-codebase. יכול להיות ש תמיד יהיה צורך להסיר את moment באפליקציה הזו, אבל מה היה קורה אם היו בו אזורי זמן ולוקאלים שונים שצריך לטפל בהם? מה יקרה אם היו שינויים מורכבים יותר בתאריכים? דברים יכולים להיות מסובכים מאוד כשמשנים ומנתחים תאריכים/שעות, וספריות כמו moment ו-date-fns מפשטות את זה משמעותית.

כל דבר הוא פתרון לא פשוט, וחשוב לבדוק אם שווה את המורכבות והמאמץ להשקת פתרון בהתאמה אישית במקום להסתמך על ספרייה של צד שלישי.