עיבוד HTML באמצעות JavaScript שונה מעיבוד HTML שנשלח על ידי השרת, והדבר יכול להשפיע על הביצועים. כדאי לקרוא על ההבדלים במדריך הזה ולגלות מה אפשר לעשות כדי לשמור על ביצועי הרינדור של האתר – במיוחד אם יש סוגיות שקשורות לאינטראקציות.
ניתוח ועיבוד של HTML הם משהו שדפדפנים עושים היטב כברירת מחדל באתרים המשתמשים בלוגיקת הניווט המובנית של הדפדפן, שלפעמים נקראת "טעינות דפים מסורתיות" או "ניווטים קשים". אתרים כאלה נקראים לפעמים 'אפליקציות מרובות דפים' (MPA).
עם זאת, המפתחים יכולים לעקוף את ברירות המחדל של הדפדפנים כדי שיתאימו לצורכי האפליקציות שלהם. זה בהחלט המצב באתרים שמשתמשים בתבנית של אפליקציית דף יחיד (SPA), שיוצרת באופן דינמי חלקים גדולים של ה-HTML/DOM בלקוח באמצעות JavaScript. 'רינדור בצד הלקוח' הוא השם של דפוס העיצוב הזה, והוא יכול להשפיע על האינטראקציה בין הצבעים הבאים (INP) של האתר אם העבודה רבה מדי.
מדריך זה יעזור לך לשקול את ההבדל בין שימוש ב-HTML שנשלח על ידי השרת לדפדפן לעומת יצירתו בלקוח באמצעות JavaScript, והאופן שבו האפשרות השנייה יכולה לגרום לזמן אחזור ארוך של אינטראקציה ברגעים קריטיים.
כיצד הדפדפן מעבד HTML שסופק על ידי השרת
דפוס הניווט שבו משתמשים בטעינות דפים רגילות כולל קבלת HTML מהשרת בכל ניווט. אם מזינים כתובת URL בסרגל הכתובות של הדפדפן או לוחצים על קישור בהודעת MPA, מתרחשים האירועים הבאים:
- הדפדפן שולח בקשת ניווט לכתובת ה-URL שצוינה.
- השרת מגיב עם HTML במקטעים.
השלב האחרון בתהליך הזה הוא המפתח. זוהי גם אחת מאופטימיזציות הביצועים הבסיסיות ביותר בחילופי השרת והדפדפן, והיא נקראת סטרימינג. אם השרת יכול להתחיל לשלוח HTML בהקדם האפשרי, והדפדפן אינו ממתין להגעת התגובה המלאה, הדפדפן יכול לעבד HTML במקטעים כאשר הוא מגיע.
כמו רוב הדברים שקורים בדפדפן, ניתוח HTML מתרחש בתוך משימות. כש-HTML עובר בסטרימינג מהשרת לדפדפן, הדפדפן מבצע אופטימיזציה של ניתוח ה-HTML על ידי כך שהוא עושה זאת קצת אחרי רגע, כשקטעים מהזרם מגיעים במקטעים. כתוצאה מכך, הדפדפן חוזר ל-thread הראשי מדי פעם אחרי העיבוד של כל מקטע, וכך נמנעים ממשימות ארוכות. פירוש הדבר הוא שבמהלך ניתוח ה-HTML, עשויות להתרחש עבודות אחרות, כולל עבודת העיבוד המצטברת שנדרשת כדי להציג דף למשתמש, וכן עיבוד אינטראקציות של משתמשים שעשויות להתרחש במהלך תקופת ההפעלה הקריטית של הדף. גישה זו מובילה לציון טוב יותר של Interaction to Next Paint (INP) בדף.
והטייק אוויי? כשמשדרים HTML מהשרת, מקבלים ניתוח ורינדור מצטבר של HTML ומופק באופן אוטומטי בחינם ל-thread הראשי. אי אפשר להבין את זה עם רינדור בצד הלקוח.
כיצד הדפדפן מעבד HTML שסופק על ידי JavaScript
למרות שכל בקשת ניווט לדף מחייבת לספק כמות מסוימת של HTML על ידי השרת, חלק מהאתרים ישתמשו בדפוס SPA. גישה זו כוללת בדרך כלל מטען ייעודי (payload) ראשוני מינימלי של ה-HTML שסופק על ידי השרת, אבל אז הלקוח יאכלס את אזור התוכן הראשי של הדף ב-HTML שמורכב מנתונים שאוחזרו מהשרת. הניווטים הבאים, שבמקרה הזה מכונים לפעמים 'ניווטים רכים', מטופלים ב-JavaScript באופן מלא, כדי לאכלס את הדף ב-HTML חדש.
עיבוד בצד הלקוח עשוי להתרחש גם במקומות שאינם SPA, במקרים מוגבלים יותר שבהם HTML נוסף באופן דינמי ל-DOM באמצעות JavaScript.
יש כמה דרכים נפוצות ליצירת HTML או להוספה ל-DOM באמצעות JavaScript:
- הנכס
innerHTML
מאפשר להגדיר את התוכן ברכיב קיים באמצעות מחרוזת, שהדפדפן מנתח ל-DOM. - השיטה
document.createElement
מאפשרת ליצור רכיבים חדשים שיתווספו ל-DOM בלי להשתמש בניתוח HTML של הדפדפן. - השיטה
document.write
מאפשרת לכתוב HTML במסמך (והדפדפן מנתח אותו, בדיוק כמו בגישה מס' 1). עם זאת, מכמה סיבות, לא מומלץ מאוד להשתמש ב-document.write
.
ההשלכות של יצירת HTML/DOM באמצעות JavaScript בצד הלקוח עשויות להיות משמעותיות:
- שלא כמו HTML שמשודר על ידי השרת בתגובה לבקשת ניווט, משימות JavaScript בלקוח לא מחולקות למקטעים באופן אוטומטי, מה שעלול לגרום למשימות ארוכות שחוסמות את ה-thread הראשי. פירוש הדבר הוא שה-INP של הדף שלך עלול להיפגע אם תיצור יותר מדי HTML/DOM בבת אחת אצל הלקוח.
- אם HTML נוצר בלקוח במהלך ההפעלה, משאבים שיש אליהם הפניה לא לא יתגלו על ידי סורק הטעינה מראש של הדפדפן. לבחירה הזו תהיה בהחלט השפעה שלילית על המהירות שבה נטען רכיב התוכן הכי גדול (LCP) בדף. אמנם לא מדובר בבעיה בביצועי זמן ריצה (אלא מדובר בבעיה של עיכוב ברשת באחזור משאבים חשובים), אבל אינך רוצה שה-LCP באתר שלך יושפע מביצוע אופטימיזציה בסיסית זו של ביצועי הדפדפן.
מה אפשר לעשות לגבי ההשפעה של רינדור בצד הלקוח על הביצועים
אם הרינדור בצד הלקוח תלוי בעיקר ברינדור בצד הלקוח, והבחנתם בערכי INP חלשים בנתוני השדות, אולי תהיתם אם הרינדור בצד הלקוח קשור לבעיה. לדוגמה, אם האתר שלכם הוא SPA, נתוני השדות עשויים לחשוף אינטראקציות שאחראיות ליצירה משמעותית של עיבוד תמונה.
לא משנה מה הסיבה, הנה כמה סיבות אפשריות שכדאי לך לבדוק כדי לחזור למצב הרגיל.
לספק כמה שיותר HTML מהשרת
כפי שהוזכר קודם לכן, כברירת מחדל הדפדפן מטפל ב-HTML מהשרת בדרך יעילה מאוד. התכונה הזו תפרק את הניתוח והעיבוד של HTML באופן שימנע משימות ארוכות, ותבצע אופטימיזציה של משך הזמן הכולל של ה-thread הראשי. המצב הזה מוביל לזמן חסימה כולל (TBT) נמוך יותר, ול-TBT יש התאמה גבוהה ל-INP.
ייתכן שאתם מסתמכים על מסגרת קצה לבניית האתר שלכם. אם כן, כדאי לוודא שאתה מעבד את ה-HTML של הרכיב בשרת. המדיניות הזו תגביל את כמות הרינדור הראשוני בצד הלקוח שתזדקקו לו, ואתם תיהנו מחוויה טובה יותר.
- ב-React, כדאי להשתמש ב-Server DOM API כדי לעבד HTML בשרת. אבל חשוב לזכור: בשיטה המסורתית של רינדור בצד השרת נעשה שימוש בגישה סינכרונית, שעשויה להוביל ל-Time to First Byte (TTFB) ארוך יותר, וכן למדדים נוספים כמו הצגת תוכן ראשוני (FCP) ו-LCP. כשהדבר אפשרי, הקפידו להשתמש בממשקי ה-API לסטרימינג עבור Node.js או בזמני ריצה אחרים של JavaScript כדי שהשרת יוכל להתחיל להזרים HTML לדפדפן בהקדם האפשרי. Next.js – מסגרת שמבוססת על תגובה – מספקת שיטות מומלצות רבות כברירת מחדל. בנוסף לעיבוד אוטומטי של HTML בשרת, הוא יכול גם ליצור באופן סטטי HTML לדפים שלא משתנים על סמך ההקשר של המשתמש (כגון אימות).
- Vue מבצע גם רינדור בצד הלקוח כברירת מחדל. עם זאת, כמו React, Vue יכול גם לעבד את ה-HTML של הרכיב בשרת. מומלץ לנצל את ממשקי ה-API בצד השרת כשאפשר, או לשקול הפשטה ברמה גבוהה יותר של פרויקט Vue כדי ליישם את השיטות המומלצות בקלות רבה יותר.
- Svelte מעבד HTML בשרת כברירת מחדל - למרות שאם קוד הרכיב שלך זקוק לגישה למרחבי שמות בלעדיים לדפדפן (
window
, לדוגמה), ייתכן שלא תוכל לעבד את ה-HTML של הרכיב הזה בשרת. בדקו גישות חלופיות ככל האפשר, כדי לא לגרום לעיבוד מיותר בצד הלקוח. SvelteKit – ב-Svelte בתור Next.js – React – מטמיע שיטות מומלצות רבות ככל האפשר בפרויקטים ב-Svelte, כדי להימנע מבעיות פוטנציאליות בפרויקטים שמשתמשים ב-Svelte בלבד.
הגבלת כמות צומתי DOM שנוצרו אצל הלקוח
כש-DOM גדול, משך זמן העיבוד שנדרש לעיבוד שלהם נוטה להתארך. אם האתר שלכם הוא SPA מלא, או אם הוא מחדיר צמתים חדשים ל-DOM קיים כתוצאה מאינטראקציה עם MPA, כדאי לשקול לשמור את ה-DOM קטן ככל האפשר. כך תצמצמו את העבודה הנדרשת במהלך העיבוד בצד הלקוח כדי להציג את ה-HTML הזה, בתקווה שזה יעזור לשמור על ערך INP נמוך יותר של האתר.
כדאי לשקול ארכיטקטורה של קובץ שירות סטרימינג
זוהי טכניקה מתקדמת, שייתכן שלא ניתן להתאים אותה בקלות לכל תרחיש לדוגמה, אבל היא יכולה להפוך את הבקשה לפעולה עם MPA לאתר שנראה כאילו הוא נטען באופן מיידי כשמשתמשים עוברים מדף לדף. אתם יכולים להשתמש ב-Service Worker כדי לשמור מראש את החלקים הסטטיים של האתר ב-CacheStorage
, ולהשתמש ב-API של ReadableStream
כדי לאחזר מהשרת את שאר רכיבי ה-HTML של דף.
כאשר אתם משתמשים בשיטה הזו בהצלחה, אתם לא יוצרים HTML בלקוח, אבל טעינה מיידית של חלקי תוכן מהמטמון תיצור את הרושם שהאתר שלכם נטען במהירות. אתרים שמשתמשים בגישה הזו יכולים להרגיש כמעט כמו SPA, אבל בלי החסרונות של עיבוד בצד הלקוח. הוא גם מפחית את כמות ה-HTML שאתם מבקשים מהשרת.
בקיצור, ארכיטקטורת worker של שירותי סטרימינג לא מחליפה את לוגיקת הניווט המובנית של הדפדפן, אלא מוסיפה אליה. מידע נוסף על הדרך לעשות זאת באמצעות Workbox זמין במאמר אפליקציות מהירות יותר במספר דפים עם סטרימינג.
סיכום
האופן שבו האתר שלך מקבל ומעבד HTML משפיע על הביצועים. כשאתם מסתמכים על כך שהשרת ישלח את כל קוד ה-HTML הדרוש כדי שהאתר שלכם יפעל, אתם מקבלים הרבה בחינם: ניתוח ועיבוד מצטבר ותפוקה אוטומטית ל-thread הראשי, כדי להימנע ממשימות ארוכות.
עיבוד HTML בצד הלקוח יוצר מספר בעיות אפשריות בביצועים שניתן להימנע מהן במקרים רבים. עם זאת, עקב הדרישות של כל אתר בנפרד, לא ניתן להימנע לגמרי 100% מהזמן. כדי לצמצם את המשימות הארוכות הפוטנציאליות שעלולות להיגרם מעיבוד יתר של אתר הלקוח, יש להקפיד לשלוח כמה שיותר מה-HTML של האתר מהשרת, ככל האפשר, להקפיד שגודל ה-DOM יהיה קטן ככל האפשר עבור HTML שצריך לעבד בלקוח, ולשקול ארכיטקטורות חלופיות כדי לזרז את מסירת ה-HTML ללקוח תוך ניצול היתרונות של הניתוח והעיבוד המצטברים שהדפדפן מספק עבור HTML שנטען מהשרת.
אם אתם יכולים שהרינדור בצד הלקוח יהיה מינימלי ככל האפשר, תשפרו לא רק את ה-INP של האתר, אלא גם מדדים אחרים כמו LCP, TBT ואולי אפילו ה-TTDFB במקרים מסוימים.
תמונה ראשית (Hero) מ-UnFlood, מאת Maik Jonitz.