אין להילחם בסורק הטעינה מראש של הדפדפן

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

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

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

לכל דפדפן יש מנתח HTML ראשי שמחלק את התגים לסמלים (tokenizes) של תגי עיצוב גולמיים ומעבד אותם למודל אובייקטים. התהליך הזה נמשך עד שהמנתח מושהה כשהוא מוצא משאב חסימה, כמו גיליון סגנונות שנטען עם אלמנט <link> או סקריפט שנטען עם אלמנט <script> ללא מאפיין async או defer.

דיאגרמה של מנתח HTML.
איור 1: תרשים שמראה איך אפשר לחסום את מנתח ה-HTML הראשי של הדפדפן. במקרה כזה, המנתח נתקל ברכיב <link> של קובץ CSS חיצוני, שחוסם את הדפדפן מנתח את שאר המסמך – או אפילו להציג חלק ממנו – עד שה-CSS יוריד וינותח.

במקרה של קובצי CSS, המערכת חוסמת את העיבוד כדי למנוע הצגה קצרה של תוכן ללא עיצוב (FOUC). מצב כזה מתרחש כשאפשר לראות גרסה ללא עיצוב של דף למשך זמן קצר לפני החלת הסגנונות עליו.

דף הבית של web.dev במצב ללא עיצוב (שמאל) ובמצב עם עיצוב (ימין).
איור 2: דוגמה מדומה ל-FOUC. בצד ימין מוצגת דף הבית של web.dev ללא סגנונות. בצד שמאל מוצג אותו דף עם סגנונות שהוחלו עליו. המצב ללא עיצוב יכול להתרחש במהירות אם הדפדפן לא חוסם את העיבוד בזמן שהקובץ של גיליון הסגנונות מוריד ומעובד.

הדפדפן חוסם גם את הניתוח והעיבוד של הדף כשהוא נתקל ברכיבי <script> ללא מאפיין defer או async.

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

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

תרשים של מנתח ה-HTML הראשי (שמאל) וסורק ההטענה מראש (ימין), שהוא מנתח ה-HTML המשני.
איור 3: תרשים שבו מוצגת האופן שבו סורק ההטענה מראש פועל במקביל למנתח ה-HTML הראשי כדי לטעון נכסים באופן ספקולטיבי. כאן, מנתח ה-HTML הראשי חסום בזמן שהוא טוען ומעבד CSS לפני שהוא יכול להתחיל לעבד את תגי העיצוב של התמונה ברכיב <body>. עם זאת, סורק ההטענה מראש יכול להסתכל קדימה בתגי העיצוב הגולמיים כדי למצוא את משאב התמונה הזה ולהתחיל לטעון אותו לפני שהחסימה של מנתח ה-HTML הראשי תוסר.

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

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

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

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

בתרשים המפל של הרשת ב-WebPageTest מוצג עיכוב מלאכותי של 2 שניות שהוטל על הגיליון של העיצוב.
איור 4: תרשים רשת של מפל ב-WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד דרך חיבור 3G מדומה. למרות שגיליון הסגנונות מתעכב באופן מלאכותי דרך שרת proxy במשך שתי שניות לפני שהוא מתחיל לטעון, הסורק של טעינת הנתונים מראש מזהה את התמונה שנמצאת בהמשך של עומס העבודה של ה-Markup.

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

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

סקריפטים מושתלים של async

נניח שיש לכם קטע HTML ב-<head> שכולל קצת JavaScript בקוד, כמו זה:

<script>
 
const scriptEl = document.createElement('script');
  scriptEl
.src = '/yall.min.js';

  document
.head.appendChild(scriptEl);
</script>

סקריפטים מוזרקים הם async כברירת מחדל, כך שכשהסקריפט הזה יוזרק, הוא יתנהג כאילו המאפיין async הוחל עליו. המשמעות היא שהיא תפעל בהקדם האפשרי ולא תחסום את העיבוד. נשמע אופטימלי, נכון? עם זאת, אם נניח שה-<script> הזה מוטמע בקוד אחרי רכיב <link> שטעון קובץ CSS חיצוני, התוצאה תהיה לא אופטימלית:

בתרשים הזה של WebPageTest מוצגת סריקה מראש שנכשלה כשהוחדר סקריפט.
איור 5: תרשים רשימת גורמים של רשת ב-WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד דרך חיבור 3G מדומה. הדף מכיל גיליון סגנונות אחד וסקריפט async שהוזן. סורק ההטענה מראש לא יכול לזהות את הסקריפט במהלך שלב החסימה של הרינדור, כי הוא מוזרק ללקוח.

בואו נסביר מה קרה:

  1. בשנייה 0, מתבצעת בקשה למסמך הראשי.
  2. אחרי 1.4 שניות, מגיע הבייט הראשון של בקשת הניווט.
  3. אחרי 2.0 שניות, מתבצעת בקשה ל-CSS ולתמונה.
  4. מאחר שהמנתח חסום לטעינת גיליון הסגנונות, וקוד ה-JavaScript המוטמע שמחדיר את הסקריפט async מגיע אחרי גיליון הסגנונות הזה, אחרי 2.6 שניות, הפונקציונליות שהסקריפט מספק לא זמינה בהקדם האפשרי.

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

אז מה קורה אם משתמשים בתג <script> רגיל עם המאפיין async במקום להחדיר את הסקריפט ל-DOM?

<script src="/yall.min.js" async></script>

זו התוצאה:

רשימת אירועים של WebPageTest לרשת שמראה איך סקריפט אסינכררוני שנטען באמצעות רכיב הסקריפט של HTML עדיין ניתן לגילוי על ידי סורק ההטענה מראש של הדפדפן, למרות שמנתח ה-HTML הראשי של הדפדפן חסום בזמן ההורדה והעיבוד של גיליון סגנונות.
איור 6: תרשים רשימת גורמים של רשת ב-WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד דרך חיבור 3G מדומה. הדף מכיל גיליון סגנונות אחד ורכיב async <script> אחד. הסורק של טעינת הנתונים מראש מזהה את הסקריפט במהלך שלב החסימה של הרינדור, וטעון אותו בו-זמנית עם ה-CSS.

יכול להיות שתהיה נטייה להציע שאפשר לפתור את הבעיות האלה באמצעות rel=preload. זה אכן יפעל, אבל יכולות להיות לכך תופעות לוואי. אחרי הכל, למה להשתמש ב-rel=preload כדי לפתור בעיה שאפשר למנוע על ידי לא להחדיר רכיב <script> ל-DOM?

רשימת מקורות ב-WebPageTest שמראה איך המערכת משתמשת בהצעה של המשאב rel=preload כדי לקדם את הזיהוי של סקריפט שהוזן באופן אסינכרוני – אם כי באופן שעלול להיות לו תופעות לוואי לא רצויות.
איור 7: תרשים רשימת גורמים של רשת ב-WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד דרך חיבור 3G מדומה. הדף מכיל גיליון סגנונות אחד וסקריפט async שהוזן, אבל הסקריפט async נטען מראש כדי להבטיח שהוא יתגלה מוקדם יותר.

טעינת נתונים מראש 'פותרת' את הבעיה הזו, אבל היא יוצרת בעיה חדשה: הסקריפט async בשני הדמואים הראשונים – למרות שהוא נטען ב-<head> – נטען בעדיפות 'נמוכה', בעוד שגיליון הסגנונות נטען בעדיפות 'הגבוהה ביותר'. בדמו האחרון שבו הסקריפט async נטען מראש, גיליון הסגנונות עדיין נטען בעדיפות 'הגבוהה ביותר', אבל העדיפות של הסקריפט שודרגה ל'גבוהה'.

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

התשובה היא פשוטה: אם צריך סקריפט במהלך ההפעלה, אל תכשילו את סורק ההטענה מראש על ידי הזרקה שלו ל-DOM. מומלץ להתנסות לפי הצורך במיקום הרכיב <script>, וגם במאפיינים כמו defer ו-async.

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

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

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

<img data-src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

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

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

תרשים רשת של WebPageTest שמראה איך תמונה שנטענת באיטיות ונמצאת בחלון התצוגה במהלך ההפעלה מתעכבת באופן הכרחי כי סורק הטעינה מראש של הדפדפן לא מצליח למצוא את משאב התמונה, והיא נטענת רק כשה-JavaScript שנדרש לטעינה האיטית נטען. התמונה מתגלה הרבה יותר מאוחר מכפי שהיא אמורה.
איור 8: תרשים רשימת גורמים של WebPageTest לרשת של דף אינטרנט שפועל ב-Chrome במכשיר נייד דרך חיבור 3G מדומה. משאב התמונה נטען באיטרציה מיותרת, למרות שהוא גלוי באזור התצוגה בזמן ההפעלה. הפעולה הזו מבטלת את הסריקה של טעינת הנתונים מראש וגורמת לעיכוב מיותר.

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

הפתרון הוא לשנות את ה-Markup של התמונה:

<img src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

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

תרשים רשת של WebPageTest שבו מוצג תרחיש טעינה של תמונה בחלון התצוגה במהלך ההפעלה. התמונה לא נטענת באופן עצל, כלומר היא לא תלויה בסקריפט כדי להיטען, ולכן הסורק לטעינה מראש יכול לזהות אותה מוקדם יותר.
איור 9: תרשים רשימת גורמים של רשת WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד בחיבור 3G מדומה. סורק ההטענה מראש מזהה את משאב התמונה לפני שה-CSS וה-JavaScript מתחילים להיטען, וכך נותן לדפדפן יתרון מוקדם לטעינת המשאב.

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

תמונות רקע ב-CSS

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

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

נניח שהמועמד ל-LCP של הדף הוא רכיב עם מאפיין CSS‏ background-image. במהלך טעינת המשאבים מתרחשים הדברים הבאים:

תרשים רשימת משימות של רשת ב-WebPageTest שמוצג בו דף עם נכס LCP שטעון מ-CSS באמצעות המאפיין background-image. מכיוון שהתמונה של ה-LCP היא מסוג משאב שסורק ההטענה מראש בדפדפן לא יכול לבדוק, טעינת המשאב מתעכבת עד שה-CSS מוריד ומעובד, וכך זמן הצביעה של ה-LCP מתעכב.
איור 10: תרשים רשימת גורמים של רשת WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד דרך חיבור 3G מדומה. המועמד ל-LCP של הדף הוא רכיב עם מאפיין CSS‏ background-image (שורה 3). האחזור של התמונה המבוקשת מתחיל רק אחרי שמנתח ה-CSS מוצא אותה.

במקרה כזה, סורק הטעינה מראש לא מפסיק לפעול, אלא פשוט לא מעורב. עם זאת, אם אחד מהמועמדים ל-LCP בדף מגיע מנכס CSS מסוג background-image, כדאי לטעון מראש את התמונה הזו:

<!-- Make sure this is in the <head> below any
     stylesheets, so as not to block them from loading -->

<link rel="preload" as="image" href="lcp-image.jpg">

ההצעה rel=preload קטנה, אבל היא עוזרת לדפדפן לגלות את התמונה מוקדם יותר מאשר אחרת:

תרשים רשת של WebPageTest שבו מוצגת תמונת רקע ב-CSS (זוהי התמונה האפשרית ל-LCP) שנטענת הרבה יותר מהר בגלל שימוש בהנחיה rel=preload. זמן ה-LCP משתפר בכ-250 אלפיות השנייה.
איור 11: תרשים רשימת גורמים של רשת ב-WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד בחיבור 3G מדומה. המועמד ל-LCP של הדף הוא רכיב עם מאפיין CSS‏ background-image (שורה 3). ההצעה rel=preload עוזרת לדפדפן לזהות את התמונה כ-250 אלפיות השנייה מוקדם יותר מאשר ללא ההצעה.

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

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

הטמעה בקוד היא שיטה שבה משאב ממוקם בתוך ה-HTML. אפשר להוסיף גיליונות סגנונות בתוך שורות (inline) באלמנטים מסוג <style>, סקריפטים באלמנטים מסוג <script> ולכל משאב אחר כמעט באמצעות קידוד base64.

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

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

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

תרשים רשת של WebPageTest לדף עם קובץ CSS חיצוני עם ארבעה גופנים שמצוינים בו. סורק ההטענה מראש יזהה את התמונה האפשרית ל-LCP בזמן המתאים.
איור 12: תרשים רשימת גורמים של WebPageTest לרשת של דף אינטרנט שפועל ב-Chrome במכשיר נייד דרך חיבור 3G מדומה. המועמד ל-LCP של הדף הוא תמונה שנטענת מרכיב <img>, אבל הוא מתגלה על ידי סורק ההטענה מראש כי ה-CSS והגופנים הנדרשים לטעינת הדף נמצאים במשאבים נפרדים, כך שסורק ההטענה מראש לא מתעכב בביצוע עבודתו.

מה קורה אם ה-CSS וגם כל הגופנים מוטמעים כמשאבי base64?

תרשים רשת של WebPageTest לדף עם קובץ CSS חיצוני עם ארבעה גופנים שמצוינים בו. יש עיכוב משמעותי בגילוי תמונת ה-LCP על ידי סורק הטעינה מראש .
איור 13: תרשים רשימת גורמים של רשת ב-WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד בחיבור 3G מדומה. המועמדת ל-LCP של הדף היא תמונה שנטענת מרכיב <img>, אבל הטמעת הקוד של ה-CSS וארבעת משאבי הגופן שלו ב-`` ‎ גורמת לסריק הטעינה מראש לגלות את התמונה רק אחרי שהמשאבים האלה יורדים במלואם.

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

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

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

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

עיבוד תגים באמצעות JavaScript בצד הלקוח

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

דפוס אחד שיכול לעקוף את סורק העומס מראש הוא עיבוד תגים באמצעות JavaScript בצד הלקוח:

תרשים Waterfall של רשת WebPageTest שבו מוצג דף בסיסי עם תמונות וטקסט שעבר עיבוד מלא בצד הלקוח ב-JavaScript. מאחר שהתווים האלה נכללים ב-JavaScript, סורק הטעינה מראש לא יכול לזהות אף אחד מהמשאבים. כל המשאבים מתעכבים עוד יותר בגלל זמן הטיפול והזמן הנוסף שנדרש ברשת במסגרות JavaScript.
איור 14: תרשים רשת של WebPageTest לדף אינטרנט שעבר עיבוד על ידי הלקוח שפועל ב-Chrome במכשיר נייד בחיבור 3G מדומה. מכיוון שהתוכן נכלל ב-JavaScript ומסתמך על מסגרת לעיבוד, משאב התמונה בסימני ה-Markup שעובדו על ידי הלקוח מוסתר מסורק הטעינה מראש. חוויית המשתמש המקבילה עם עיבוד בצד השרת מופיעה באיור 9.

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

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

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

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

איך עוזרים לסורק של טעינה מראש לעזור לכם

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

לסיכום, אלה הדברים שחשוב לזכור מהפוסט הזה:

  • סורק הטעינה מראש של הדפדפן הוא מנתח HTML משני שסורק לפני המנתח הראשי אם הוא חסום, כדי לזהות באופן אופורטוניסטי משאבים שהוא יכול לאחזר מוקדם יותר.
  • סורקי ההטענה מראש לא יכולים לזהות משאבים שלא נמצאים בסימני ה-Markup שסופקו על ידי השרת בבקשת הניווט הראשונית. דרכים שבהן אפשר לעקוף את סורק ההטענה מראש כוללות, בין היתר:
    • הזרקת משאבים ל-DOM באמצעות JavaScript, בין אם מדובר בסקריפטים, בתמונות, בסגנונות גיליון או בכל דבר אחר שעדיף להוסיף לעומס התעבורה הראשוני של ה-Markup מהשרת.
    • טעינת פריטים לאט (lazy loading) של תמונות או תגי iframe מעל למסך באמצעות פתרון JavaScript.
    • עיבוד רכיבי קוד בלקוח שעשויים להכיל הפניות למשאבי משנה של מסמכים באמצעות JavaScript.
  • סורק ההטענה מראש סורק רק HTML. הוא לא בודק את התוכן של משאבים אחרים, במיוחד CSS, שעשויים לכלול הפניות לנכסים חשובים, כולל נכסים שעומדים בקריטריונים ל-LCP.

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

משאבים

התמונה הראשית (Hero) מ-Unsplash, מאת Mohammad Rahmani .