עיבוד בצד הלקוח של HTML ואינטראקטיביות

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

ניתוח ועיבוד של HTML הם פעולה שדפדפנים פועלים היטב כברירת מחדל באתרים שמשתמשים בלוגיקת הניווט המובנית של הדפדפן – לפעמים נקראת 'טעינות דפים מסורתיות' או 'ניווטים קשים'. אתרים כאלה נקראים לפעמים 'אפליקציות עם מספר דפים' (MPA).

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

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

איך הדפדפן מעבד את ה-HTML שהשרת מספק

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

  1. הדפדפן שולח בקשת ניווט לכתובת ה-URL שצוינה.
  2. השרת מגיב עם HTML במקטעי נתונים.

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

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

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

  1. המאפיין innerHTML מאפשר להגדיר את התוכן של רכיב קיים באמצעות מחרוזת, שהדפדפן מנתח ל-DOM.
  2. השיטה document.createElement מאפשרת ליצור רכיבים חדשים שיתווספו ל-DOM בלי להשתמש בניתוח HTML בדפדפן.
  3. השיטה document.write מאפשרת לכתוב HTML למסמך (והדפדפן מנתח אותו, בדיוק כמו בגישה הראשונה). עם זאת, יש כמה סיבות לכך, מומלץ מאוד להשתמש ב-document.write.
צילום מסך של ניתוח HTML שעבר רינדור באמצעות JavaScript, ללא המחשה בחלונית הביצועים של כלי הפיתוח ל-Chrome. העבודה מתבצעת במשימה ארוכה אחת שחוסמת את ה-thread הראשי.
ניתוח ורינדור של HTML באמצעות JavaScript בצד הלקוח, כפי שמוצג בחלונית הביצועים של כלי הפיתוח ל-Chrome. המשימות הכרוכות בניתוח וברינדור של התוכן לא מחולקות למקטעי נתונים, וכתוצאה מכך נוצרת משימה ארוכה שחוסמת את ה-thread הראשי.

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

כדאי להשתמש בארכיטקטורת Worker של שירות סטרימינג

זו שיטה מתקדמת, שאולי לא מתאימה לכל תרחיש, אבל היא יכולה להפוך את אישור השימוש ב-MPA לאתר שנראה כאילו הוא נטען מיד כשהמשתמשים עוברים מדף לדף. אתם יכולים להשתמש ב-Service Worker כדי לשמור מראש את החלקים הסטטיים של האתר ב-CacheStorage ולהשתמש ב-ReadableStream API כדי לאחזר את שאר קוד ה-HTML של דף מהשרת.

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

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

סיכום

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

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

אם תוכלו לשפר ככל האפשר את הרינדור של האתר בצד הלקוח, תוכלו לשפר לא רק את מדד ה-INP של האתר, אלא גם מדדים אחרים כמו LCP, TBT ואולי גם אפילו את מדד ה-TTDFB.

תמונה ראשית (Hero) מ-Unbounce, מאת Maik Jonitz.