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

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

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

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