הטמעת משאבים במסגרות JavaScript

שיפור המדד Largest Contentful Paint בסביבת JavaScript.

כחלק מפרויקט Aurora, Google עבדה עם מסגרות אינטרנט פופולריות כדי לוודא שהן מניבות ביצועים טובים בהתאם למדדים הבסיסיים של חוויית המשתמש (Core Web Vitals). כבר הוספנו את האפשרות להטמיע פונטים בקוד ב-Angular וב-Next.js, כפי שמוסבר בחלק הראשון של המאמר. האופטימיזציה השנייה שנעסוק בה היא הטמעת CSS חיונית, שמופעלת עכשיו כברירת מחדל ב-Agular CLI ושנמצאת בשלבי הטמעה ב-Nuxt.js.

הטמעת גופן בקוד

אחרי ניתוח של מאות אפליקציות, צוות Aurora מצא שיזמים כוללים גופנים באפליקציות שלהם לעיתים קרובות על ידי הפניה אליהם ברכיב <head> של index.html. הדוגמה הבאה ממחישה איך זה ייראה כשכוללים Material Icon:

<!doctype html>
<html lang="en">
<head>
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  ...
</html>

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

/* fallback */
@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}

.material-icons {
  /*...*/
}

שימו לב שההגדרה של font-face מפנה לקובץ חיצוני שמתארח ב-fonts.gstatic.com. כשהאפליקציה נטענת, הדפדפן צריך קודם להוריד את גיליון הסגנונות המקורי שמוזכר בכותרת.

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

לאחר מכן, הדפדפן מוריד את הקובץ woff2, ובסופו של דבר הוא יכול להמשיך ברינדור של האפליקציה.

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

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

כשמפתחים את האפליקציה, נשלחת בקשה ל-CDN, שמאחזר את גיליון הסגנונות ומוסיף אותו לקובץ ה-HTML, תוך הוספת <link rel=preconnect> לדומיין. אם נשתמש בשיטה הזו, נקבל את התוצאה הבאה:

<!doctype html>
<html lang="en">
<head>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
  <style type="text/css">
  @font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
  ...
</html>

הטמעת גופנים בקוד זמינה עכשיו ב-Next.js וב-Angular

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

השיפור הזה מופעל כברירת מחדל מ-Next.js v10.2 ומ-Agular v11. בשתיהן יש תמיכה בהטמעת גופנים של Google ו-Adobe. Angular צפויה להשיק את האפשרות השנייה בגרסה 12.2.

אפשר למצוא את ההטמעה של הטמעת גופן ב-Next.js ב-GitHub, ולצפות בסרטון שמסביר את האופטימיזציה הזו בהקשר של Angular.

הטמעת CSS קריטי בקוד

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

שמנו לב שאפליקציות רבות טוענות סגנונות באופן סינכרוני, מה שגורם לחסימה של עיבוד האפליקציה. פתרון מהיר הוא לטעון את הסגנונות באופן אסינכרוני. במקום לטעון את הסקריפטים באמצעות media="all", מגדירים את הערך של המאפיין media כ-print, ולאחר השלמת הטעינה מחליפים את ערך המאפיין ל-all:

<link rel="stylesheet" href="..." media="print" onload="this.media='all'">

עם זאת, השיטה הזו עלולה לגרום להבהוב של תוכן לא מעוצב.

הדף מתחיל להבהב בזמן טעינת הסגנונות.

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

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

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

נבחן את הדוגמה הבאה:

מה אסור לעשות
<head>
   <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>
/* styles.css */
section button.primary {
  /* ... */
}
.list {
  /* ... */
}

דוגמה לפני הטמעה בקוד.

בדוגמה שלמעלה, המערכת תקריא ותנתח את התוכן של styles.css, ואז תתאים את שני הבוררים ל-HTML ותגלה שאנחנו משתמשים ב-section button.primary. לבסוף, ה-critters יציגו את הסגנונות התואמים ב-<head> של הדף, וכתוצאה מכך:

מה מותר לעשות
<head>
  <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
  <style>
  section button.primary {
    /* ... */
  }
  </style>
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>

דוגמה לאחר הטמעה בקוד.

אחרי שמזינים את קוד ה-CSS הקריטי ב-HTML, אפשר לראות שההבהוב של הדף נעלם:

טעינה של הדף אחרי הטמעת ה-CSS.

הטמעת CSS קריטי בקוד זמינה עכשיו ב-Angular ומופעל כברירת מחדל בגרסה 12. אם אתם משתמשים בגרסה 11, כדי להפעיל אותה, צריך להגדיר את inlineCritical כ-true ב-angular.json. כדי להביע הסכמה לשימוש בתכונה הזו ב-Next.js, מוסיפים את experimental: { optimizeCss: true } ל-next.config.js.

מסקנות

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

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