שליפה מראש (prefetch) של משאבים כדי להאיץ את הניווטים בעתיד

מידע על הטיפים לגבי משאבים מסוג rel=prefetch ואיך משתמשים בהם

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

במדריך הזה נסביר איך לעשות זאת באמצעות <link rel=prefetch>, רמז למשאב שמאפשר ליישם אחסון נתונים לפני אחזור (prefetching) בצורה קלה ויעילה.

הוספת <link rel=prefetch> לדף אינטרנט מורה לדפדפן להוריד דפים שלמים או חלק מהמשאבים (כמו סקריפטים או קובצי CSS) שהמשתמש עשוי להזדקק להם בעתיד:

<link rel="prefetch" href="/articles/" as="document">

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

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

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

תרחישים לדוגמה

אחזור מראש של דפים הבאים

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

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

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

אחזור מראש של נכסים סטטיים

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

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

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

  • טעינה מראש של תמונות יכולה לצמצם באופן משמעותי את זמני ה-LCP של רכיבי תמונה מסוג LCP.
  • אחזור מראש של גיליונות סגנונות יכול לשפר את ה-FCP ואת ה-LCP, כי זמן ההורדה של גיליון הסגנונות מהרשת יימנע. גיליונות סגנונות חוסמים את העיבוד, ולכן הם יכולים לצמצם את ה-LCP כשהם נשלפים מראש. במקרים שבהם רכיב ה-LCP של הדף הבא הוא תמונת רקע של CSS שנשלחה באמצעות המאפיין background-image, התמונה תיטען מראש גם כמשאב תלוי של גיליון הסגנונות שנטען מראש.
  • אחזור מראש של JavaScript יאפשר את העיבוד של הסקריפט שנאגר מראש הרבה יותר מוקדם מאשר אם הרשת הייתה צריכה לאחזר אותו קודם במהלך הניווט. הדבר עשוי להשפיע על זמן האינטראקציה עד התוכן הבא (INP) של הדף. במקרים שבהם ה-Markup מנוהל בצד הלקוח באמצעות JavaScript, אפשר לשפר את ה-LCP על ידי צמצום עיכובי הטעינה של המשאבים, והרינדור של ה-Markup בצד הלקוח שמכיל את רכיב ה-LCP של הדף יכול להתרחש מוקדם יותר.
  • אחזור מראש של גופנים אינטרנטיים שעדיין לא נמצאים בשימוש בדף הנוכחי יכול למנוע שינויים בפריסה. במקרים שבהם נעשה שימוש ב-font-display: swap;, תקופת המעבר של הגופן מבוטלת, וכתוצאה מכך העיבוד של הטקסט מהיר יותר ולא מתרחשים שינויי פריסה. אם בדף עתידי נעשה שימוש בגופן שנטען מראש, ורכיב ה-LCP של הדף הוא בלוק של טקסט שמשתמש בגופן אינטרנט, גם זמן ה-LCP של האלמנט הזה יהיה מהיר יותר.

אחזור מראש של קטעי JavaScript על פי דרישה

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

לדוגמה, אם יש לכם דף שמכיל לחצן שפותח תיבת דו-שיח שמכילה בורר אמוג'י, אתם יכולים לחלק אותו לשלושה קטעי JavaScript – home, ‏ dialog ו-picker. אפשר לטעון את דף הבית ואת תיבת הדו-שיח בשלב הראשון, ואת הבורר אפשר לטעון על פי דרישה. כלים כמו webpack מאפשרים להורות לדפדפן לבצע אחסון מראש של קטעי הקוד האלה על פי דרישה.

איך מטמיעים את rel=prefetch

הדרך הפשוטה ביותר להטמיע את prefetch היא להוסיף תג <link> ל-<head> של המסמך:

<head>
  ...
  <link rel="prefetch" href="/articles/" as="document">
  ...
</head>

המאפיין as עוזר לדפדפן להגדיר את הכותרות הנכונות ולקבוע אם המשאב כבר נמצא במטמון. דוגמאות לערכים של המאפיין הזה: document, script, style, font, image ו-others.

אפשר גם להתחיל את האחזור מראש באמצעות כותרת ה-HTTP ‏Link:

Link: </css/style.css>; rel=prefetch

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

אחזור מראש של מודולים של JavaScript באמצעות תגובות קסם של webpack

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

קטע הקוד הבא טוען באופן עצל (lazy) פונקציונליות מיון מהספרייה lodash כדי למיין קבוצת מספרים שתישלח באמצעות טופס:

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

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

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

כך מבקשים מ-webpack להחדיר את התג <link rel="prefetch"> למסמך ה-HTML:

<link rel="prefetch" as="script" href="1.bundle.js">

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

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

אפשר גם להטמיע אחסון מקדים חכם יותר באמצעות ספריות שמשתמשות ב-prefetch ברקע:

  • הספרייה quicklink משתמשת ב-Intersection Observer API כדי לזהות מתי קישורים נכנסים למסך, ומבצעת אחסון מראש של משאבים מקושרים במהלך זמן ההמתנה. בונוס: קישור מהיר שוקל פחות מ-1KB!
  • Guess.js משתמש בדוחות ניתוח נתונים כדי ליצור מודל חיזוי שמשמש לאחזור מראש חכם של מה שהמשתמש צפוי להזדקק לו.

גם quicklink וגם Guess.js משתמשים ב-Network Information API כדי להימנע משיוך מראש אם המשתמש נמצא ברשת איטית או שהאפשרות Save-Data מופעלת.

שליפה מראש (prefetch) ברקע

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

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

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

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

סיכום

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