כללים והמלצות של PageSpeed

Ilya Grigorik
Ilya Grigorik

תאריך פרסום: 17 באוגוסט 2018

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

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

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

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

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

הימנעות מבקשות שרתי סינכרוניות

משתמשים בשיטה navigator.sendBeacon() כדי להגביל את הנתונים שנשלחים על ידי הודעות XMLHttpRequest בטיפולי unload. הרבה דפדפנים דורשים שבקשות כאלה יהיו סינכרוניות, ולכן הן עלולות להאט מעברי דפים, לפעמים באופן משמעותי. בקוד הבא מוסבר איך להשתמש ב-navigator.sendBeacon() כדי לשלוח נתונים לשרת במטפל pagehide במקום במטפל unload.

<script>
  function() {
    window.addEventListener('pagehide', logData, false);
    function logData() {
      navigator.sendBeacon(
        'https://putsreq.herokuapp.com/Dt7t2QzUkG18aDTMMcop',
        'Sent by a beacon!');
    }
  }();
</script>

השיטה fetch() מספקת דרך טובה יותר לבקש נתונים באופן אסינכרוני. fetch() מעבד תגובות באמצעות Promises במקום מספר פונקציות טיפול באירועים. בניגוד לתגובה של XMLHttpRequest, תגובה של fetch() היא אובייקט של מקור נתונים (stream). המשמעות היא שקריאה ל-json() מחזירה גם היא Promise.

<script>
  fetch('./api/some.json')
    .then(
      function(response) {
        if (response.status !== 200) {
          console.log('Looks like there was a problem. Status Code: ' +  response.status);
          return;
        }
        // Examine the text in the response
        response.json().then(function(data) {
          console.log(data);
        });
      }
    )
    .catch(function(err) {
      console.log('Fetch Error :-S', err);
    });
</script>

השיטה fetch() יכולה לטפל גם בבקשות POST.

<script>
  fetch(url, {
    method: 'post',
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  }).then(function() { // Additional code });
</script>

דחיית ניתוח JavaScript

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

הימנעות משימוש ב-JavaScript ארוך טווח

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

אופטימיזציה של השימוש בשירות CSS

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

מקם CSS בראש המסמך

יש לציין את כל משאבי ה-CSS בהקדם האפשרי במסמך ה-HTML, כדי שהדפדפן יוכל לגלות את תגי <link> ולשלוח את הבקשה אל ה-CSS בהקדם האפשרי.

הימנעות מייבוא של CSS

ההוראה לייבוא CSS‏ (@import) מאפשרת לגיליון סגנונות אחד לייבא כללים מקובץ גיליון סגנונות אחר. עם זאת, כדאי להימנע מהוראות האלה כי הן מוסיפות עוד נסיעות הלוך ושוב לנתיב הקריטי: משאבי ה-CSS המיובאים מתגלים רק אחרי שגיליון הסגנונות של ה-CSS עם הכלל @import עצמו מתקבל ומנותח.

CSS מוטבע לחסימת עיבוד

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

משוב