Codelab: טעינה מראש של נכסים קריטיים כדי לשפר את מהירות הטעינה

בקודלאב הזה משפרים את הביצועים של דף האינטרנט הבא באמצעות טעינת נתונים מראש (preload) של כמה משאבים ושליפה מראש (prefetch) שלהם:

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

מדידה

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

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

מריצים את ביקורת הביצועים של Lighthouse (Lighthouse > Options > Performance) בגרסה הפעילה של Glitch (ראו גם זיהוי הזדמנויות לשיפור הביצועים באמצעות Lighthouse).

מערכת Lighthouse מציגה את הביקורת שנכשלה הבאה לגבי משאב שאוחזר באיחור:

Lighthouse: ביקורת של בקשות מפתח לטעינה מראש
  • מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את DevTools.
  • לוחצים על הכרטיסייה רשתות.
חלונית 'רשת' עם משאב שזוהה מאוחר

קובץ main.css לא מאוחזר על ידי אלמנט קישור (<link>) שמופיע במסמך ה-HTML, אלא על ידי קובץ JavaScript נפרד, fetch-css.js, שמצרף את אלמנט הקישור ל-DOM אחרי האירוע window.onLoad. כלומר, האחזור של הקובץ מתבצע רק אחרי שהדפדפן מסיים לנתח ולבצע את קובץ ה-JS. באופן דומה, אחזור של פונט אינטרנט (K2D.woff2) שצוין ב-main.css מתבצע רק אחרי שההורדה של קובץ ה-CSS מסתיימת.

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

├─┬ / (initial HTML file)
  └── fetch-css.js
    └── main.css
      └── K2D.woff2

מכיוון שקובץ ה-CSS נמצא ברמה השלישית של שרשרת הבקשות, מערכת Lighthouse זיהתה אותו כמשאב שהתגלה מאוחר.

טעינה מוקדמת של משאבים קריטיים

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

מוסיפים תג טעינה מראש לאפליקציה הזו:

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
</head>

המאפיין as משמש לזיהוי סוג המשאב שאוחזר, והמאפיין as="style" משמש לטעינת קבצים של גיליונות סגנונות לפני השימוש בהם.

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

חלונית &#39;רשת&#39; עם משאב טעון מראש

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

אם לא משתמשים בה בצורה נכונה, טעינת הנתונים מראש עלולה להזיק לביצועים על ידי שליחת בקשות מיותרות למשאבים שלא נמצאים בשימוש. באפליקציה הזו, details.css הוא קובץ CSS נוסף שנמצא ברמה הבסיסית של הפרויקט, אבל משמש ל-/details route נפרד. כדי להראות דוגמה לשימוש שגוי בטעינה מראש, נוסיף גם למשאב הזה רמז לטעינה מראש.

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
  <link rel="preload" href="details.css" as="style">
</head>

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

חלונית &#39;רשת&#39; עם טעינה מראש מיותרת

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

אזהרה על טעינה מראש במסוף

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

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
  <link rel="preload" href="details.css" as="style">
</head>

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

אחזור מראש של משאבים עתידיים

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

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

פרטי המסלול

קובץ CSS נפרד, details.css, מכיל את כל הסגנונות הנדרשים לדף הפשוט הזה. כדי לבצע אחסון מראש של המשאב הזה, מוסיפים רכיב קישור אל index.html.

<head>
  <!-- ... -->
  <link rel="prefetch" href="details.css">
</head>

כדי להבין איך הפעולה הזו מפעילה בקשה לקובץ, פותחים את החלונית Network ב-DevTools ומבטלים את הסימון של האפשרות Disable cache.

השבתת המטמון בכלי הפיתוח ל-Chrome

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

חלונית רשת עם משאב שנאגר מראש

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

בקשות רשת בדף הפרטים

לוחצים על בקשת הרשת details.css ב-DevTools כדי להציג את הפרטים שלה. תוכלו לראות שהקובץ אוחזר ממטמון האחסון של הדפדפן.

בקשת פרטים שאוחזרה ממטמון האחסון

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

טעינה מראש ושליפה מראש (prefetch) באמצעות webpack

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

אפליקציית Magic Sorter שממחישה פיצול קוד

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

קטע הקוד הבא, שנמצא ב-src/index.js,, אחראי על ייבוא הדינמי של השיטה כשלוחצים על הלחצן.

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

פיצול חבילה משפרת את זמני הטעינה של הדפים על ידי צמצום הגודל הראשוני שלה. גרסה 4.6.0 של webpack מספקת תמיכה לטעינת קטעים מראש או לטעינת קטעים מראש (prefetch) שיובאו באופן דינמי. לדוגמה, באפליקציה הזו אפשר לאחזר מראש את השיטה lodash בזמן שהדפדפן לא פעיל. כך, כשהמשתמש לוחץ על הלחצן, אין עיכוב באחזור המשאב.

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

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

אחרי הטעינה מחדש של האפליקציה, webpack מזין תג prefetch של המשאב בחלק העליון של המסמך. אפשר לראות את זה בחלונית Elements ב-DevTools.

חלונית רכיבים עם תג prefetch

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

חלונית הרשת עם בקשה שאוחזרה מראש

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

import(/* webpackPreload: true */ 'module')

סיכום

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

לסיכום:

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

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

למידע נוסף על האופן שבו טעינת נתונים מראש וטעינת נתונים מראש (prefetching) יכולים להשפיע על דף האינטרנט, אפשר לעיין במאמרים הבאים: