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

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

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

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

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

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

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

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

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

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

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