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

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

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

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

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

תרשים ה-Waterfall של רשת 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?

Waterfall של 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. כשגוללים את התמונה לאזור התצוגה, הטוען העצלני מסיר את הקידומת 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.

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

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

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

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

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

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

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

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

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

משאבים

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