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

שיפור ה-LCP ברחבי המערכת האקולוגית של JavaScript.

כחלק מהפרויקט Aurora, Google עובדת עם מסגרות אינטרנט פופולריות כדי להבטיח שהן יניבו ביצועים טובים בהתאם למדדי הליבה לבדיקת חוויית המשתמש באתר. Angular ו-Next.js כבר הציגו גופן בתוך שורה, כפי שמוסבר בחלק הראשון של מאמר זה. האופטימיזציה השנייה שנעסוק בה היא הטמעה קריטית של CSS, שכרגע מופעלת כברירת מחדל ב-Angular 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

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

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

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

הטבעת CSS קריטי

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

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

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

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

נראה שהדף מהבהב כשהסגנונות נטענים.

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

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

הטמעה קריטית של CSS, יחד עם טעינת סגנון אסינכרונית, יכולה לשפר את התנהגות הטעינה. הכלי יצורים מוצא באילו סגנונות נעשה שימוש בדף על ידי עיון בסלקטורים בגיליון סגנונות והתאמתם ל-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. לבסוף, היצורים יטמיעו את הסגנונות המתאימים ב-<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. אם אתם משתמשים ב-v11, הפעילו אותה על ידי הגדרת הנכס inlineCritical ל-true ב-angular.json. כדי להפעיל את התכונה הזו ב-Next.js, צריך להוסיף את experimental: { optimizeCss: true } ל-next.config.js.

מסקנות

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

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