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

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

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

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

מדידה

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

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

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

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

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

אופטימיזציה

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

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

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

שתי האפשרויות 3 ו-4 עוזרות לשפר את ביצועי האפליקציה. בחלקים הבאים ב-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>
  `
});

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

חלונית רשת שבה מוצגת חבילת 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) שכוללת את היכולת לייבא מודול באופן דינמי. חבילת האינטרנט כבר כוללת תמיכה בכך, לפי אותו תחביר שנקבע בהצעה.

הפונקציה import() מחזירה את הערך של ההבטחה, ואחרי שהיא פותרת את הבעיה, המודול שנבחר מחולק למקטע נפרד. אחרי החזרת המודול, נעשה שימוש ב-module.default כדי להפנות לברירת המחדל לייצוא של lodash. ההבטחה משורשרת עם .then אחר שקורא ל-method 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. ברגע שהאפליקציה נטענת, מתבצעת הורדה של חבילה ראשונית קטנה בלבד.

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

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

חלונית רשת שבה מוצגת חבילת JavaScript בנפח 2.7KB ואחריה חבילת JavaScript בנפח 13.9KB.

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

סיכום

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

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

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

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

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

טעינה מדורגת עם מסגרת של JavaScript

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

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

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

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

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

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