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

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

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

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

מה זה סורק טרום-טעינה?

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

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

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

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

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

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

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

הוספת יותר מדי משאבים בשורת קוד

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

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

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

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

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

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

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

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

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

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

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

משאבים

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