תיקון חוסר יציבות בפריסה

מדריך לשימוש ב-WebPageTest כדי לזהות בעיות של חוסר יציבות בפריסת האתר ולתקן אותן.

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

באמצעות Layout Instability API, אנחנו יכולים לקבל רשימה של כל אירועי השינוי בפריסה בדף:

new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(list.getEntries().filter(entry => !entry.hadRecentInput));
  }).observe({type: "layout-shift", buffered: true});
}).then(console.log);

הפעולה הזו יוצרת מערך של שינויי פריסה שלא מקדימים אותם אירועי קלט:

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 210.78500000294298,
    "duration": 0,
    "value": 0.0001045969445437389,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

בדוגמה הזו, היה שינוי קטן מאוד של 0.01% ב-210ms.

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

מדידת שינויי פריסה ב-WebPageTest

בדומה למדידת CLS ב-WebPageTest, כדי למדוד שינויים נפרדים בפריסת הדף צריך להשתמש במדד מותאם אישית. למרבה המזל, הגרסה של Chrome 77 יציבה יותר כי התהליך קל יותר. ממשק Layout Instability API מופעל כברירת מחדל, כך שאפשר להריץ את קטע הקוד הזה ב-JS בכל אתר ב-Chrome 77 ולקבל תוצאות באופן מיידי. ב-WebPageTest, אפשר להשתמש בדפדפן Chrome שמוגדר כברירת מחדל ולא צריך לדאוג לגבי דגלים בשורת הפקודה או להשתמש ב-Canary.

עכשיו נשנה את הסקריפט הזה כדי ליצור מדד מותאם אישית ל-WebPageTest:

[LayoutShifts]
return new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(JSON.stringify(list.getEntries().filter(entry => !entry.hadRecentInput)));
  }).observe({type: "layout-shift", buffered: true});
});

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

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

זיהוי הסיבות לחוסר היציבות של הפריסה

בקטע results אפשר לראות שהערך של המדד המותאם אישית LayoutShifts הוא:

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3087.2349999990547,
    "duration": 0,
    "value": 0.3422101449275362,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

לסיכום, יש שינוי יחיד בפריסה של 34.2% שמתרחש ב-3,087 אלפיות השנייה. כדי לזהות את הגורם לבעיה, נשתמש בתצוגת סרטון של WebPageTest.

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

כשגוללים לסמן של כ-3 שניות בסרטון, אפשר לראות בדיוק מה גורם לשינוי של 34% בפריסת המודעות: הטבלה הצבעונית. האתר מאחזר קובץ JSON באופן אסינכרוני, ואז מעבד אותו לטבלה. הטבלה ריקה בהתחלה, ולכן ההמתנה למילוי שלה כשהתוצאות נטענות גורמת לשינוי.

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

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

תיקון של חוסר יציבות בפריסה

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

זהו הקוד ליצירת נתוני placeholder:

function getRandomFiller(maxLength) {
  var filler = '█';
  var len = Math.ceil(Math.random() * maxLength);
  return new Array(len).fill(filler).join('');
}

function getRandomDistribution() {
  var fast = Math.random();
  var avg = (1 - fast) * Math.random();
  var slow = 1 - (fast + avg);
  return [fast, avg, slow];
}

// Temporary placeholder data.
window.data = [];
for (var i = 0; i < 36; i++) {
  var [fast, avg, slow] = getRandomDistribution();
  window.data.push({
    platform: getRandomFiller(10),
    client: getRandomFiller(5),
    n: getRandomFiller(1),
    fast,
    avg,
    slow
  });
}
updateResultsTable(sortResults(window.data, 'fast'));

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

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

כך נראים הסמנים בזמן טעינת נתוני ה-JSON:

טבלת הנתונים עוברת עיבוד עם נתוני placeholder.
טבלת הנתונים עוברת עיבוד באמצעות נתוני placeholder.

פתרון הבעיה של גופן האינטרנט פשוט הרבה יותר. מכיוון שהאתר משתמש ב-Google Fonts, אנחנו צריכים רק להעביר את המאפיין display=swap בבקשת ה-CSS. זה הכול. Fonts API יוסיף את הסגנון font-display: swap בהצהרת הגופן, וכך הדפדפן יוכל ליצור גרפיקה של הטקסט בגופן החלופי באופן מיידי. זהו תגי העיצוב התואמים עם התיקון:

<link href="https://fonts.googleapis.com/css?family=Chivo:900&display=swap" rel="stylesheet">

אימות האופטימיזציות

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

פסקול של WebPageTest שבו מוצגים שני האתרים נטענים זה לצד זה עם או בלי אופטימיזציה של הפריסה.
רצועת תמונות של WebPageTest שבה רואים את שני האתרים שנטענים זה לצד זה עם ובלי אופטימיזציית פריסה.
[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3070.9349999997357,
    "duration": 0,
    "value": 0.000050272187989256116,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

לפי המדד המותאם אישית, עדיין יש שינוי בפריסת הדף שמתרחש אחרי 3,071 אלפיות השנייה (כמעט באותו זמן כמו קודם), אבל חומרת השינוי קטנה בהרבה: 0.005%. אני יכול לחיות עם זה.

בנוסף, ברור מהסרטון שהפונט <h1> עובר מיד לפונט מערכת, וכך המשתמשים יכולים לקרוא אותו מוקדם יותר.

סיכום

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

(עוד דבר) מדידת חוסר היציבות של הפריסה שנחשף אליו על ידי משתמשים אמיתיים

טוב מאוד שאתם יכולים להריץ את WebPageTest בדף לפני ואחרי האופטימיזציה ולראות שיפור במדד, אבל מה שחשוב באמת הוא שחוויית המשתמש למעשה משתפרת. האם זה לא הסיבה לכך שאנחנו מנסים לשפר את האתר מלכתחילה?

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

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