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

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

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

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

מהו סורק טעינה מראש?

לכל דפדפן יש כלי ניתוח ראשי של 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 של הרשת איפה פועל סורק הטעינה מראש.

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

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

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

החדרת async סקריפטים

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

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

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

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

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

זו התוצאה:

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

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

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

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

הפתרון הוא לשנות את תגי העיצוב של התמונה:

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

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

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

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

תמונות רקע של שירות CSS

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

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

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

תרשים מפל של רשת WebPageTest שבו מוצג דף עם מועמד LCP שנטען מ-CSS באמצעות המאפיין של תמונת רקע. מאחר שהתמונה של מועמד ה-LCP נמצאת בסוג משאב שסורק הטעינה מראש של הדפדפן לא יכול לבדוק, טעינת המשאב מתעכבת עד להורדה ולעיבוד של ה-CSS, ולכן זמן העיבוד של מועמד ה-LCP מתעכב.
איור 10: תרשים Waterfall של רשת 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: תרשים Waterfall של רשת WebPageTest של דף אינטרנט שפועל ב-Chrome במכשיר נייד דרך סימולציה של חיבור 3G. אלמנט ה-LCP של הדף הוא רכיב עם מאפיין CSS מסוג background-image (שורה 3). הרמז rel=preload עוזר לדפדפן לגלות את התמונה כ-250 אלפיות שנייה יותר מאשר בלי הרמז.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

משאבים

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