איך גדלים של DOM גדולים משפיעים על האינטראקטיביות ומה אפשר לעשות בקשר אליה

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

אין מנוס: כשאתם יוצרים דף אינטרנט, הדף הזה יכלול Document Object Model‏ (DOM). ה-DOM מייצג את המבנה של ה-HTML בדף, ומעניק ל-JavaScript ול-CSS גישה למבנה ולתוכן של הדף.

עם זאת, הבעיה היא שהגודל של DOM משפיע על היכולת של הדפדפן ליצור עיבוד (render) של דף במהירות וביעילות. באופן כללי, ככל שה-DOM גדול יותר, כך יקר יותר העיבוד של הדף הזה והעיבוד שלו בשלב מאוחר יותר במחזור החיים של הדף.

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

מתי DOM של דף גדול מדי?

לפי Lighthouse, גודל ה-DOM של דף גדול מדי כשהוא חורג מ-1,400 צמתים. מערכת Lighthouse תתחיל להציג אזהרות כשה-DOM של דף מסוים יחרוג מ-800 צמתים. ניקח לדוגמה את קוד ה-HTML הבא:

<ul>
  <li>List item one.</li>
  <li>List item two.</li>
  <li>List item three.</li>
</ul>

בקוד שלמעלה יש ארבעה רכיבי DOM: הרכיב <ul> ושלושת רכיבי הצאצא שלו מסוג <li>. כמעט בטוח שיהיו בדף האינטרנט הרבה יותר צמתים מאלה, לכן חשוב להבין מה אפשר לעשות כדי לשלוט בגודל ה-DOM, וגם אסטרטגיות אחרות לאופטימיזציה של עיבוד התצוגה אחרי שה-DOM של הדף קטן ככל האפשר.

איך רכיבי DOM גדולים משפיעות על ביצועי הדפים?

יש כמה דרכים שבהן DOM גדול משפיע על ביצועי הדף:

  1. במהלך העיבוד הראשוני של הדף. כשמחילים CSS על דף, נוצר מבנה שדומה ל-DOM שנקרא מודל אובייקטים של CSS (CSSOM). ככל שסלקטורים של CSS נעשים ספציפיים יותר, ה-CSSOM נעשה מורכב יותר, ונדרש יותר זמן כדי להריץ את פעולות הפריסה, העיצוב, הקישור והצביעה הנדרשות כדי לצייר את דף האינטרנט במסך. העבודה הנוספת הזו מאריכה את זמן האחזור של אינטראקציות שמתרחשות בשלב מוקדם במהלך טעינת הדף.
  2. כשאינטראקציות משנות את ה-DOM, בין שבאמצעות הוספה או מחיקה של רכיבים ובין שבאמצעות שינוי של תוכן וסטייל של DOM, העבודה הנדרשת כדי להציג את העדכון הזה עלולה לגרום לעלויות גבוהות מאוד של פריסה, עיצוב, קומפוזיציה וצבע. בדומה לעיבוד הגרפי הראשוני של הדף, הגדלת הספציפיות של בוררי ה-CSS יכולה להגדיל את עומס העיבוד הגרפי כשרכיבי HTML מוכנסים ל-DOM כתוצאה מאינטראקציה.
  3. כש-JavaScript שולחת שאילתות ל-DOM, הפניות לרכיבי DOM מאוחסנות בזיכרון. לדוגמה, אם קוראים ל-document.querySelectorAll כדי לבחור את כל הרכיבים מסוג <div> בדף, עלות הזיכרון עשויה להיות גבוהה אם התוצאה מחזירה מספר גדול של רכיבי DOM.
צילום מסך של משימה ארוכה שנגרמה בגלל עיבוד יתר בחלונית הביצועים של כלי הפיתוח ל-Chrome. בסטאק הקריאות של המשימה הארוכה מוצג זמן משמעותי שחולף על חישוב מחדש של סגנונות הדף, וגם על צביעה מראש.
משימה ארוכה כפי שמוצגת בפרופיל הביצועים בכלי הפיתוח ל-Chrome. המשימה הארוכה שמוצגת נגרמת על ידי הוספת רכיבי DOM ל-DOM גדול באמצעות JavaScript.

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

איך אפשר למדוד את גודל ה-DOM?

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

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

document.querySelectorAll('*').length;

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

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

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

איך אפשר למדוד את מספר רכיבי ה-DOM שמושפעים מאינטראקציה?

אם אתם יוצרים פרופיילינג של אינטראקציה איטית בשיעור ה-Lab ושאתם חושדים שקשורה לגודל ה-DOM של הדף, תוכלו להבין כמה רכיבי DOM הושפעו על ידי בחירה של קטע פעילות כלשהו ב-Profiler שמסומנת בתווית 'Recalculate Style' (חישוב מחדש של סגנון) וצפייה בנתוני ההקשר בחלונית התחתונה.

צילום מסך של הפעילות שנבחרה לחישוב מחדש של הסגנון בחלונית הביצועים של כלי הפיתוח ל-Chrome. בחלק העליון, מסלול האינטראקציות מציג אינטראקציה עם קליק, ורוב העבודה מושקעת בחישוב מחדש של הסגנון ובעבודה מראש. בחלק התחתון מוצגת חלונית עם פרטים נוספים על הפעילות שנבחרה, ומפורט בה ש-2,547 רכיבי DOM הושפעו.
מעקב אחרי מספר הרכיבים שהושפעו ב-DOM כתוצאה מעבודת החישוב מחדש של הסגנון. שימו לב שהחלק המוצלל של האינטראקציה בנתוני המעקב אחר האינטראקציות מייצג את החלק מתוך משך האינטראקציה שהיה ארוך מ-200 אלפיות השנייה, שהוא הסף 'הטוב' שנקבע ל-INP.

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

איך אפשר להקטין את נפח ה-DOM?

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

<div>
  <div>
    <div>
      <div>
        <!-- Contents -->
      </div>
    </div>
  </div>
</div>

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

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

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

באמצעות שימוש במקטעים במסגרת שבחרתם, אפשר להפחית את עומק ה-DOM. אם אתם מודאגים מההשפעה של שטיחת מבנה ה-DOM על העיצוב, כדאי לכם להשתמש במצבי פריסה מודרניים (ומהירים יותר) כמו flexbox או grid.

אסטרטגיות נוספות שכדאי לשקול

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

כדאי לשקול גישה מצטברת

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

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

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

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

הגבלת המורכבות של סלקטורים ב-CSS

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

שימוש במאפיין content-visibility

ב-CSS יש את המאפיין content-visibility, שהוא למעשה דרך לבצע עיבוד גרפי (render) עצל של רכיבי DOM מחוץ למסך. כשהרכיבים מתקרבים למסך, הם עוברים עיבוד על פי דרישה. היתרונות של content-visibility לא רק גוררים כמות משמעותית של עבודת רינדור ברינדור של הדף הראשוני, אלא גם מדלגים על עבודת הרינדור של רכיבים שלא מופיעים במסך כשה-DOM של הדף משתנה כתוצאה מאינטראקציה של משתמש.

סיכום

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

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