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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

רינדור תגי עיצוב עם JavaScript בצד הלקוח

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

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

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

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

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

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

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

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

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

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

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

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

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

משאבים

תמונה ראשית (Hero) מתוך UnFlood, מאת מוחמד רחמני .