הפחתת מטענים ייעודיים (payloads) של JavaScript באמצעות פיצול קוד

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

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

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

מדידה

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

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

חלונית הרשת מציגה חבילת JavaScript בגודל 71.2KB.

‫71.2KB של JavaScript רק כדי למיין כמה מספרים באפליקציה פשוטה. מה קורה?

במקור הקוד (src/index.js), ספריית lodash מיובאת ונעשה בה שימוש באפליקציה הזו. ‫Lodash מספקת הרבה פונקציות שימושיות, אבל כאן נעשה שימוש רק בשיטה אחת מהחבילה. טעות נפוצה היא התקנה וייבוא של תלות מלאה בצד שלישי, כשמשתמשים רק בחלק קטן ממנה.

אופטימיזציה

יש כמה דרכים לצמצם את גודל החבילה:

  1. כתיבת שיטת מיון מותאמת אישית במקום לייבא ספרייה של צד שלישי
  2. שימוש בשיטה המובנית Array.prototype.sort() למיון מספרי
  3. ייבוא רק של השיטה sortBy מ-lodash ולא של כל הספרייה
  4. הורדת הקוד למיון רק כשהמשתמש לוחץ על הלחצן

אפשרויות 1 ו-2 הן שיטות מתאימות מאוד להקטנת גודל החבילה (וכנראה שהן הכי הגיוניות לאפליקציה אמיתית). אבל לא נשתמש בהם במדריך הזה כדי להסביר את הנושא 😈.

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

ייבוא רק של מה שצריך

צריך לשנות כמה קבצים כדי לייבא רק את השיטה היחידה מ-lodash. קודם כל, מחליפים את התלות הזו ב-package.json:

"lodash": "^4.7.0",

עם הנתונים הבאים:

"lodash.sortby": "^4.7.0",

עכשיו, ב-src/index.js, מייבאים את המודול הספציפי הזה:

import "./style.css";
import _ from "lodash";
import sortBy from "lodash.sortby";

כדי לעדכן את אופן המיון של הערכים:

form.addEventListener("submit", e => {
  e.preventDefault();
  const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
  const sortedValues = _.sortBy(values);
  const sortedValues = sortBy(values);

  results.innerHTML = `
    <h2>
      ${sortedValues}
    </h2>
  `
});

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

חלונית הרשת שבה מוצג חבילת JavaScript בגודל 15.2KB.

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

פיצול קוד

webpack הוא אחד מכלי האריזה הפופולריים ביותר של מודולים בקוד פתוח שמשמשים כיום. בקיצור, הוא מאגד את כל מודולי ה-JavaScript (וגם נכסים אחרים) שמרכיבים אפליקציית אינטרנט לקבצים סטטיים שהדפדפן יכול לקרוא.

אפשר לפצל את החבילה היחידה שמשמשת באפליקציה הזו לשני חלקים נפרדים:

  • אחד שאחראי לקוד שמרכיב את המסלול הראשוני שלנו
  • חלק משני שמכיל את קוד המיון שלנו

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

מתחילים בהסרת הייבוא ברמה העליונה של שיטת המיון ב-src/index.js:

import sortBy from "lodash.sortby";

מייבאים אותו בתוך event listener שמופעל כשלוחצים על הלחצן:

form.addEventListener("submit", e => {
  e.preventDefault();
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

התכונה import() היא חלק מהצעה (בשלב 3 בתהליך TC39) לכלול את היכולת לייבא מודול באופן דינמי. webpack כבר כולל תמיכה בתכונה הזו, והתחביר שלה זהה לזה שמופיע בהצעה.

import() מחזירה הבטחה, וכשהיא נפתרת, מודול נבחר מסופק, והוא מפולח לחלק נפרד. אחרי שהמודול מוחזר, נעשה שימוש ב-module.default כדי להפנות לייצוא שמוגדר כברירת מחדל על ידי lodash. ההבטחה משורשרת עם עוד .then שקוראת לשיטה sortInput כדי למיין את שלושת ערכי הקלט. בסוף שרשרת ההבטחות, .‫catch() משמש לטיפול במקרים שבהם הבטחה נדחית בגלל שגיאה.

הדבר האחרון שצריך לעשות הוא לכתוב את השיטה sortInput בסוף הקובץ. הערך צריך להיות פונקציה שמחזירה פונקציה שמקבלת את השיטה המיובאת מ-lodash.sortBy. אחר כך הפונקציה המקוננת יכולה למיין את שלושת ערכי הקלט ולעדכן את ה-DOM.

const sortInput = () => {
  return (sortBy) => {
    const values = [
      input1.valueAsNumber,
      input2.valueAsNumber,
      input3.valueAsNumber
    ];
    const sortedValues = sortBy(values);

    results.innerHTML = `
      <h2>
        ${sortedValues}
      </h2>
    `
  };
}

מעקב

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

חלונית Network (רשת) שבה מוצג חבילת JavaScript בגודל 2.7KB.

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

חלונית Network (רשת) שמציגה חבילת JavaScript בגודל 2.7KB ואחריה חבילת JavaScript בגודל 13.9KB.

שימו לב שהמספרים עדיין ממוינים!

סיכום

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

טעינה מדורגת של ממשק המשתמש

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

טעינה מדורגת של מודולי צומת של צד שלישי

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

טעינה מדורגת באמצעות JavaScript framework

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

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

טעינה מראש ושליפה מראש

במקרים שבהם אפשר, כדאי להשתמש ברמזים לדפדפן כמו <link rel="preload"> או <link rel="prefetch"> כדי לנסות לטעון מודולים קריטיים אפילו מוקדם יותר. webpack תומך בשני הרמזים באמצעות שימוש בהערות קסם בהצהרות ייבוא. הנושא הזה מוסבר בפירוט במדריך בנושא טעינה מראש של נתחים קריטיים.

טעינה מדורגת של יותר מקוד

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