אופטימיזציה של הטעינה והרינדור של WebFont

Ilya Grigorik
Ilya Grigorik

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

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

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

התנהגות ברירת המחדל

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

נתיב רינדור קריטי של גופן

  1. הדפדפן מבקש את מסמך ה-HTML.
  2. הדפדפן מתחיל לנתח את התשובה ב-HTML ולבנות את ה-DOM.
  3. הדפדפן מזהה קובצי CSS, ‏JS ומשאבים אחרים ושולח בקשות.
  4. הדפדפן יוצר את ה-CSSOM אחרי שכל תוכן ה-CSS מתקבל, ומשלב אותו עם עץ ה-DOM כדי ליצור את עץ הרינדור.
    • בקשות לגופנים נשלחות אחרי ש-tree ה-render מציין אילו וריאנטים של גופנים נדרשים כדי להציג את הטקסט שצוין בדף.
  5. הדפדפן מבצע את הפריסה ומציג את התוכן במסך.
    • אם הגופן עדיין לא זמין, יכול להיות שהדפדפן לא ירנדר פיקסלים של טקסט.
    • אחרי שהגופן זמין, הדפדפן מצייר את הפיקסלים של הטקסט.

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

אם תטעינו מראש גופנים של WebFonts ותשתמשו ב-font-display כדי לקבוע איך הדפדפנים יפעלו עם גופנים שאינם זמינים, תוכלו למנוע דפים ריקים ושינויי פריסה שנובעים מעומס בזמן הטעינה של הגופנים.

טעינה מראש של משאבי WebFont

אם יש סבירות גבוהה שהדף שלכם יצטרך WebFont ספציפי שמתארח בכתובת URL שאתם יודעים מראש, תוכלו להשתמש בתעדוף משאבים. שימוש ב-<link rel="preload"> יגרום להפעלת בקשה ל-WebFont בשלב מוקדם של נתיב הרינדור הקריטי, בלי צורך להמתין ליצירת ה-CSSOM.

התאמה אישית של עיכוב הרינדור של הטקסט

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

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

תמיכה בדפדפנים

  • Chrome: ‏ 60.
  • Edge: ‏ 79.
  • Firefox: 58.
  • Safari: 11.1.

מקור

בדומה להתנהגויות הקיימות של זמן קצוב לתפוגה של גופנים שחלק מהדפדפנים מטמיעים, font-display מפלח את משך החיים של הורדת גופן לשלושה תקופות עיקריות:

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

הבנת התקופות האלה מאפשרת לכם להשתמש ב-font-display כדי לקבוע איך הגופן ייגרם, בהתאם למועד שבו הוא הורדה או לא הורדה.

כדי לעבוד עם הנכס font-display, מוסיפים אותו לכללי @font-face:

@font-face {
  font-family: 'Awesome Font';
  font-style: normal;
  font-weight: 400;
  font-display: auto; /* or block, swap, fallback, optional */
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'), /* will be preloaded */
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range: U+000-5FF; /* Latin glyphs */
}

נכון לעכשיו, font-display תומך בטווח הערכים הבא:

  • auto
  • block
  • swap
  • fallback
  • optional

מידע נוסף על טעינה מראש של גופנים ועל המאפיין font-display זמין בפוסטים הבאים:

Font Loading API

כשמשתמשים ב-<link rel="preload"> וב-CSS font-display ביחד, אפשר לשלוט במידה רבה בחיוב על טעינת הגופן ועל הרינדור שלו, בלי להוסיף הרבה עלות ריבית. עם זאת, אם אתם צריכים התאמות אישיות נוספות, ואתם מוכנים לשאת בעלויות הנוספות של הפעלת JavaScript, יש לכם אפשרות אחרת.

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

תמיכה בדפדפנים

  • Chrome: 35.
  • Edge: ‏ 79.
  • Firefox: 41.
  • Safari: 10.

מקור

var font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", {
  style: 'normal', unicodeRange: 'U+000-5FF', weight: '400'
});

// don't wait for the render tree, initiate an immediate fetch!
font.load().then(function() {
  // apply the font (which may re-render text and cause a page reflow)
  // after the font has finished downloading
  document.fonts.add(font);
  document.body.style.fontFamily = "Awesome Font, serif";

  // OR... by default the content is hidden,
  // and it's rendered after the font is available
  var content = document.getElementById("content");
  content.style.visibility = "visible";

  // OR... apply your own render strategy here...
});

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

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

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

חובה להשתמש במטמון בצורה נכונה

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

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

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

רשימת משימות לטעינת WebFont

  • התאמה אישית של טעינת הגופן והעיבוד שלו באמצעות <link rel="preload">,‏ font-display או Font Loading API: התנהגות ברירת המחדל של טעינת נכסים בזמן אמת עלולה לגרום לעיכוב בעיבוד הטקסט. התכונות האלה בפלטפורמת האינטרנט מאפשרות לשנות את ההתנהגות הזו לגבי גופנים מסוימים, ולציין שיטות עיבוד וסף זמן קצוב מותאמים אישית לתוכן שונה בדף.
  • ציון מדיניות לאימות חוזר ולאחסון אופטימלי במטמון: גופנים הם משאבים סטטיים שמתעדכנים לעיתים רחוקות. חשוב לוודא שהשרתים מספקים חותמת זמן לטווח ארוך עם תאריך תפוגה מקסימלי ואסימון לאימות מחדש, כדי לאפשר שימוש יעיל חוזר בגופן בין דפים שונים. אם אתם משתמשים ב-service worker, כדאי להשתמש בשיטה של אחסון במטמון קודם.

בדיקה אוטומטית של התנהגות טעינת WebFont באמצעות Lighthouse

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

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