רוב דפי האינטרנט והאפליקציות מורכבים מחלקים רבים ושונים. במקום לשלוח את כל ה-JavaScript שמרכיב את האפליקציה ברגע שהדף הראשון נטען, חלוקת ה-JavaScript למספר קטעים משפרת את ביצועי הדף.
בשיעור הקוד הזה תלמדו איך להשתמש בחלוקת קוד כדי לשפר את הביצועים של אפליקציה פשוטה שממיינת שלושה מספרים.
מדידה
כמו תמיד, חשוב למדוד קודם את ביצועי האתר לפני שמנסים להוסיף אופטימיזציות.
- כדי לראות תצוגה מקדימה של האתר, לוחצים על הצגת האפליקציה. לאחר מכן לוחצים על מסך מלא .
- מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את DevTools.
- לוחצים על הכרטיסייה רשתות.
- מסמנים את התיבה Disable cache (השבתת המטמון).
- טוענים מחדש את האפליקציה.
71.2KB של JavaScript רק כדי למיין כמה מספרים באפליקציה פשוטה. מה קורה?
בקוד המקור (src/index.js
), ספריית lodash
מיובאת ומשמשת באפליקציה הזו. Lodash מספק הרבה פונקציות שימושיות, אבל כאן נעשה שימוש רק בשיטה אחת מהחבילה.
טעות נפוצה היא התקנה וייבוא של יחסי תלות שלמים של צד שלישי, שבהם נעשה שימוש רק בחלק קטן מהם.
אופטימיזציה
יש כמה דרכים לצמצם את גודל החבילה:
- כתיבת שיטת מיון מותאמת אישית במקום ייבוא ספרייה של צד שלישי
- שימוש בשיטה המובנית
Array.prototype.sort()
למיון מספרי - ייבוא רק את השיטה
sortBy
מ-lodash
ולא את הספרייה כולה - מורידים את הקוד למיון רק כשהמשתמש לוחץ על הלחצן
האפשרויות 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.
באפליקציה הזו, גודל החבילה קטן פי 4 בלי הרבה עבודה, אבל עדיין יש מקום לשיפור.
פיצול קוד
webpack הוא אחד מכלי ה-bundler הפופולריים ביותר של מודולים בקוד פתוח שנעשה בהם שימוש כיום. בקיצור, הוא מקבץ את כל המודולים של 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. מיד אחרי שהאפליקציה נטענת, מתבצעת הורדה של חבילת נתונים ראשונית קטנה בלבד.
אחרי שלוחצים על הלחצן כדי למיין את מספרי הקלט, המערכת מאחזרת ומפעילה את הקטע שמכיל את קוד המיון.
שימו לב שהמספרים עדיין ממוינים.
סיכום
פיצול קוד וטעינה איטית יכולים להיות שיטות שימושיות מאוד לצמצום גודל החבילה הראשונית של האפליקציה, וכתוצאה מכך זמני הטעינה של הדפים יהיו מהירים יותר. עם זאת, יש כמה דברים חשובים שצריך להביא בחשבון לפני שמוסיפים את האופטימיזציה הזו לאפליקציה.
ממשק משתמש עם טעינה מדורגת
כשמשתמשים בטעינה איטית של מודולים ספציפיים של קוד, חשוב להביא בחשבון איך החוויה תהיה למשתמשים עם חיבורי רשת חלשים יותר. פיצול וטעינה של קטע קוד גדול מאוד כשמשתמש שולח פעולה עלולים לגרום לכך שייראה שהאפליקציה הפסיקה לפעול, לכן מומלץ להציג אינדיקטור טעינה כלשהו.
טעינה מדורגת של מודולים של צמתים של צד שלישי
לא תמיד כדאי לטעון ב-lazy ספריות צד שלישי באפליקציה, והדבר תלוי במקומות שבהם אתם משתמשים בהן. בדרך כלל, יחסי התלות בצד שלישי מחולקים לחבילת vendor
נפרדת שאפשר לשמור במטמון, כי הם לא מתעדכנים לעיתים קרובות. מידע נוסף על האופן שבו SplitChunksPlugin יכול לעזור לכם לעשות זאת זמין כאן.
טעינה מדורגת באמצעות מסגרת JavaScript
הרבה מסגרות וספריות פופולריות שמשתמשות ב-Webpack מספקות הפשטות שבעזרתן קל יותר לבצע טעינת נתונים בזמן אמת (lazy loading) מאשר להשתמש בייבוא דינמי באמצע האפליקציה.
- טעינה מדורגת של מודולים באמצעות Angular
- פיצול קוד באמצעות React Router
- טעינה מדורגת באמצעות Vue Router
מומלץ להבין איך פועלים ייבוא דינמי, אבל תמיד צריך להשתמש בשיטה המומלצת על ידי המסגרת או הספרייה כדי לטעון באיטרציה מודולים ספציפיים.
טעינה מראש ושליפה מראש (prefetch)
במידת האפשר, כדאי להשתמש בטיפים של הדפדפן כמו <link rel="preload">
או <link rel="prefetch">
כדי לנסות לטעון מודולים קריטיים עוד יותר מוקדם. webpack תומך בשני הטיפים האלה באמצעות שימוש בתגובות קסם במשפטי ייבוא. הסבר מפורט יותר זמין במדריך טעינה מראש של קטעי קוד קריטיים.
טעינה מדורגת של יותר מקוד
תמונות יכולות להוות חלק משמעותי באפליקציה. טעינת נכסים באופן איטי (lazy loading) שנמצאים מתחת לקו הווידג'ט או מחוץ לאזור התצוגה של המכשיר יכולה לזרז את האתר. מידע נוסף זמין במדריך Lazysizes.