ניפוי באגים שינויים בפריסה

איך לזהות ולתקן שינויים בפריסה.

Katie Hempenius
Katie Hempenius

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

Layout Instability API הוא המנגנון בדפדפן למדידת שינויים בפריסה ולדיווח עליהם. כל הכלים לניפוי באגים של שינויים בפריסה, כולל DevTools, מבוססים בסופו של דבר על Layout Instability API. עם זאת, השימוש ישירות ב-Layout Instability API הוא כלי ניפוי באגים חזק בזכות הגמישות שלו.

שימוש

אותו קטע קוד שמודד את Cumulative Layout Shift‏ (CLS) יכול לשמש גם לניפוי באגים של שינויים בפריסת הדף. קטע הקוד הבא מתעד מידע על מעברי הפריסה למסוף. עיון ביומן הזה יספק לכם מידע על המיקום, המועד והאופן שבו התרחשה שינוי הפריסה.

let cls = 0;
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      cls += entry.value;
      console.log('Current CLS value:', cls, entry);
    }
  }
}).observe({type: 'layout-shift', buffered: true});

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

  • האפשרות buffered: true מציינת ש-PerformanceObserver צריך לבדוק במאגר הרשומות של הביצועים בדפדפן אם יש רשומות של ביצועים שנוצרו לפני האימות של המנטר. כתוצאה מכך, PerformanceObserver ידווח על שינויי פריסה שהתרחשו גם לפני וגם אחרי ההפעלה. חשוב לזכור זאת כשבודקים את יומני המסוף. אם יש לכם גידול משמעותי במספר השינויים הראשונים בתצוגה, יכול להיות שמדובר בבקשות דיווח שנצברו ולא באירועים רבים של שינויים בתצוגה.
  • כדי למנוע השפעה על הביצועים, ה-PerformanceObserver צריך להמתין עד שה-thread הראשי לא יהיה פעיל יותר כדי לדווח על שינויים בפריסה. כתוצאה מכך, בהתאם לעומס על החוט הראשי, יכול להיות עיכוב קל בין מועד השינוי של הפריסה לבין הרגע שבו הוא מתועד ביומן במסוף.
  • הסקריפט הזה מתעלם משינויי פריסה שהתרחשו עד 500 אלפיות השנייה לאחר הזנת קלט של משתמשים, ולכן הם לא נכללים בספירה של CLS.

הדיווח על שינויים בפריסה מתבצע באמצעות שילוב של שני ממשקי API: הממשק LayoutShift ו-LayoutShiftAttribution. כל אחד מהממשקים האלה מוסבר בפירוט בקטעים הבאים.

LayoutShift

כל שינוי פריסה מדווח באמצעות הממשק של LayoutShift. התוכן של רשומה נראה כך:

duration: 0
entryType: "layout-shift"
hadRecentInput: false
lastInputTime: 0
name: ""
sources: (3) [LayoutShiftAttribution, LayoutShiftAttribution, LayoutShiftAttribution]
startTime: 11317.934999999125
value: 0.17508567530168798

הרשומה שלמעלה מציינת שינוי בפריסה במהלך שינוי המיקום של שלושה רכיבי DOM. הציון של שינוי הפריסה הזה היה 0.175.

אלה המאפיינים של מכונה מסוג LayoutShift שרלוונטיים ביותר לניפוי באגים של שינויים בפריסה:

נכס תיאור
sources במאפיין sources מפורטים רכיבי ה-DOM שעברו במהלך שינוי הפריסה. המערך הזה יכול להכיל עד חמישה מקורות. במקרה שיש יותר מחמישה רכיבים שהושפעו משינוי הפריסה, מדווחים חמשת המקורות הגדולים ביותר (כפי שנמדד על ידי ההשפעה על יציבות הפריסה) לשינויי הפריסה. הדיווח על המידע הזה מתבצע באמצעות ממשק השיוך (Attribution) של LayoutShift (יש הסבר מפורט בהמשך).
value הנכס value מדווח על הציון של שינוי הפריסה של שינוי פריסה מסוים.
hadRecentInput המאפיין hadRecentInput מציין אם חל שינוי בפריסה תוך 500 אלפיות השנייה ממועד הקלט של המשתמש.
startTime המאפיין startTime מציין מתי התרחש שינוי בפריסה. הערך של startTime מצוין באלפיות שנייה ונמדד ביחס לזמן שבו התחיל תהליך טעינת הדף.
duration הנכס duration יוגדר תמיד בתור 0. הנכס הזה עובר בירושה מהממשק PerformanceEntry (הממשק LayoutShift מרחיב את הממשק של PerformanceEntry). עם זאת, משך הזמן לא חל על אירועים של שינוי פריסה, ולכן הוא מוגדר ל-0. מידע על הממשק של PerformanceEntry זמין במפרט.

LayoutShiftAttribution

בממשק LayoutShiftAttribution מתואר מעבר יחיד של רכיב DOM אחד. אם מספר רכיבים משתנים במהלך שינוי פריסה, המאפיין sources מכיל מספר רשומות.

לדוגמה, קובץ ה-JSON הבא תואם לשינוי פריסה עם מקור אחד: השינוי למטה של רכיב ה-DOM <div id='banner'> מ-y: 76 ל-y:246.

// ...
  "sources": [
    {
      "node": "div#banner",
      "previousRect": {
        "x": 311,
        "y": 76,
        "width": 4,
        "height": 18,
        "top": 76,
        "right": 315,
        "bottom": 94,
        "left": 311
      },
      "currentRect": {
        "x": 311,
        "y": 246,
        "width": 4,
        "height": 18,
        "top": 246,
        "right": 315,
        "bottom": 264,
        "left": 311
      }
    }
  ]

המאפיין node מזהה את רכיב ה-HTML שהוזז. כשמעבירים את העכבר מעל המאפיין הזה ב-DevTools, רכיב הדף התואם מודגש.

המאפיינים previousRect ו-currentRect מדווחים על הגודל והמיקום של הצומת.

  • הקואורדינטות x ו-y מדווחות על קואורדינטת ה-x ועל קואורדינטת ה-y של הפינה הימנית העליונה של הרכיב, בהתאמה
  • המאפיינים width ו-height מדווחים על הרוחב והגובה של האלמנט, בהתאמה.
  • המאפיינים top, right, bottom ו-left מדווחים על ערכי הקואורדינטות x או y שתואמים לקצה הנתון של הרכיב. במילים אחרות, הערך של top שווה ל-y; הערך של bottom שווה ל-y+height.

אם כל המאפיינים של previousRect מוגדרים ל-0, המשמעות היא שהרכיב עבר לתצוגה. אם כל המאפיינים של currentRect מוגדרים ל-0, המשמעות היא שהרכיב הועבר מהתצוגה.

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

דוגמה ראשונה

שינוי הפריסה הזה ידווח ממקור אחד: רכיב ב'. עם זאת, הגורם הבסיסי לשינוי הפריסה הזה הוא השינוי בגודל של הרכיב A.

דוגמה שמציגה שינוי בפריסה שנגרמה כתוצאה משינוי במאפייני הרכיב

דוגמה 2

שינוי הפריסה בדוגמה הזו ידווח משני מקורות: רכיב א' ורכיב ב'. הגורם הבסיסי לשינוי הפריסה הזה הוא השינוי במיקום של רכיב A.

דוגמה שבה מוצג שינוי פריסה שנגרם כתוצאה משינוי במיקום הרכיב

דוגמה 3

השינוי בפריסה בדוגמה הזו ידווח עם מקור אחד: רכיב ב'. שינוי המיקום של רכיב ב' גרם לשינוי הפריסה הזה.

דוגמה לשינוי פריסה שנגרמו כתוצאה משינוי במיקום הרכיב

דוגמה 4

למרות שרכיב ב' משנה את הגודל שלו, אין שינוי בפריסה בדוגמה הזו.

דוגמה שבה מוצג רכיב שמשתנה בגודל אבל לא גורם לשינוי בפריסה

בקישור הבא אפשר לראות הדגמה של אופן הדיווח של שינויי DOM על ידי Layout Instability API.

כלי פיתוח

חלונית ביצועים

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

צילום מסך של שינוי פריסה שמוצג בחלונית &#39;רשת&#39; בכלי הפיתוח

כדי להציג מידע נוסף על שינויי הפריסה, לוחצים על שינויי הפריסה ואז פותחים את התצוגה הנפתחת סיכום. השינויים במידות של הרכיב מפורטים בפורמט [width, height], השינויים במיקום הרכיב מפורטים בפורמט [x,y]. המאפיין Had recent input מציין אם שינוי בפריסה התרחש תוך 500 אלפיות השנייה ממועד האינטראקציה של המשתמש.

צילום מסך של הכרטיסייה &#39;סיכום&#39; בכלי הפיתוח לשינוי פריסה

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

צילום מסך של הכרטיסייה &#39;יומן אירועים&#39; בכלי הפיתוח לשינוי פריסה

מידע נוסף על השימוש בחלונית ביצועים זמין בחומר העזר בנושא ניתוח ביצועים.

הדגשת אזורים לשינוי הפריסה

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

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

תהליך המחשבה לזיהוי הגורם לשינויים בפריסה

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

זיהוי הסיבה לשינוי בפריסה

האירועים הבאים יכולים לגרום לשינויים בפריסה:

  • שינויים במיקום של רכיב DOM
  • שינויים במאפיינים של רכיב DOM
  • הזנה או הסרה של רכיב DOM
  • אנימציות שמפעילות את הפריסה

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

  • האם המיקום או המימדים של הרכיב הקודם השתנו?
  • האם רכיב DOM הוכנס או הוסר לפני הרכיב שהוזז?
  • האם המיקום של הרכיב שהועבר השתנה באופן מפורש?

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

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

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

אלה כמה מההתנהגויות הספציפיות שגורמות לאירועים של שינויים בפריסה בתדירות הגבוהה ביותר:

שינויים במיקום של רכיב (שאינם נובעים מהתנועה של רכיב אחר)

בדרך כלל, שינויים מהסוג הזה נובעים מהסיבות הבאות:

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

שינויים במאפייני המימדים של רכיב

סוג השינוי הזה הוא בדרך כלל תוצאה של:

  • גיליונות סגנונות שנטענים מאוחר או מבטלים סגנונות שהוצהרו בעבר.
  • תמונות ופריטי iframe ללא מאפייני width ו-height שנטענים אחרי שה'חריץ' שלהם עבר עיבוד.
  • בלוקים של טקסט ללא המאפיינים width או height שמחליפים גופנים אחרי עיבוד הטקסט.

הוספה או הסרה של רכיבי DOM

בדרך כלל, הסיבות לכך הן:

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

אנימציות שמפעילות פריסה

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

שחזור של שינויי הפריסה

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

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

new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      cls += entry.value;
      debugger;
      console.log('Current CLS value:', cls, entry);
    }
  }
}).observe({type: 'layout-shift', buffered: true});

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