אופטימיזציה של טעינת משאבים

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

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

כמו שהסברנו במודול הקודם, CSS הוא משאב render-blocking, מכיוון שהיא מונעת מהדפדפן לעבד תוכן כלשהו עד מודל האובייקט של CSS (CSSOM) בנוי. הדפדפן חוסם את העיבוד כדי למנוע הבהוב של תוכן ללא עיצוב (FOUC), דבר לא רצוי מבחינת חוויית המשתמש.

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

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

חסימת מנתח נתונים

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

<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>

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

סורק הטעינה מראש

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

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

  • התמונות נטענות על ידי שירות ה-CSS באמצעות המאפיין background-image. התמונות האלה ההפניות נמצאות ב-CSS ולא ניתן לגלות אותן באמצעות סורק הטעינה מראש.
  • סקריפטים שנטענים באופן דינמי בתור תגי עיצוב של רכיב <script> שהוחדרו ל-DOM באמצעות JavaScript או מודולים שנטענו באמצעות import() דינמי.
  • HTML שעבר רינדור אצל הלקוח באמצעות JavaScript. תגי עיצוב כאלה נמצאים בתוך מחרוזות במשאבי JavaScript, ולא ניתן לגלות אותן באמצעות הטעינה מראש בסורק של Google.
  • הצהרות @import של CSS.

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

CSS

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

הקטנה

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

/* Unminified CSS: */

/* Heading 1 */
h1 {
  font-size: 2em;
  color: #000000;
}

/* Heading 2 */
h2 {
  font-size: 1.5em;
  color: #000000;
}
/* Minified CSS: */
h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}

בצורתה הבסיסית ביותר, הקטנה של CSS היא אופטימיזציה יעילה לשפר את ה-FCP של האתר, ואולי אפילו את ה-LCP במקרים מסוימים. כלים כמו Bunders יכולים לבצע את האופטימיזציה הזו באופן אוטומטי בשבילכם בסביבת הייצור לבנות.

הסרת שירות CSS שלא נמצא בשימוש

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

כדי לאתר שירות CSS שלא נמצא בשימוש של הדף הנוכחי, אפשר להשתמש בכלי הכיסוי ב-Chrome כלי פיתוח.

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

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

הימנעות מהצהרות @import CSS

זה אמנם נראה נוח, אבל כדאי להימנע מהצהרות @import ב-CSS:

/* Don't do this: */
@import url('style.css');

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

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

<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">

ברוב המקרים, אפשר להחליף את @import באמצעות רכיב <link rel="stylesheet">. רכיבי <link> מאפשרים ליצור גיליונות סגנונות שהורדו בו-זמנית ומפחיתים את זמן הטעינה הכולל, בניגוד ל-@import שמורידות גיליונות סגנונות ברציפות.

מוטבע של CSS קריטי

הזמן שלוקח להוריד קובצי CSS יכול להגדיל את ה-FCP של דף. פנימי סגנונות קריטיים במסמך <head> מבטל את בקשת הרשת ל משאב CSS, ואם עושים זאת בצורה נכונה, יכולים לשפר את זמני הטעינה הראשונית כאשר המטמון של הדפדפן של המשתמש אינו מוגדר. אפשר לטעון את שירות ה-CSS הנותר באופן אסינכרוני, או בסוף הרכיב <body>.

<head>
  <title>Page Title</title>
  <!-- ... -->
  <style>h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}</style>
</head>
<body>
  <!-- Other page markup... -->
  <link rel="stylesheet" href="non-critical.css">
</body>

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

הדגמות של שירות CSS

JavaScript

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

JavaScript חוסם עיבוד

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

async מול defer

async ו-defer מאפשרים לסקריפטים חיצוניים להיטען בלי לחסום את ה-HTML המנתח בעוד שסקריפטים (כולל סקריפטים מוטבעים) עם type="module" שנדחו באופן אוטומטי. עם זאת, יש כמה הבדלים בין async לבין defer שחשוב להבין.

הצגה של מנגנונים שונים לטעינת סקריפטים, הכוללים תפקידי מנתח, אחזור וביצוע על סמך מאפיינים שונים כגון async, defer, type=&#39;Module ושילוב של כל השלושה.
המקור הוא https://html.spec.whatwg.org/multipage/scripting.html

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

רינדור בצד הלקוח

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

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

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

הקטנה

בדומה ל-CSS, הקטנת JavaScript מקטינה את גודל הקובץ של משאב סקריפט. דבר זה עשוי להוביל להורדות מהירות יותר, ולאפשר לדפדפן לעבור של ניתוח והידור של JavaScript במהירות רבה יותר.

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

// Unuglified JavaScript source code:
export function injectScript () {
  const scriptElement = document.createElement('script');
  scriptElement.src = '/js/scripts.js';
  scriptElement.type = 'module';

  document.body.appendChild(scriptElement);
}

כשקוד המקור של JavaScript הקודם מוצג, התוצאה עשויה להיראות משהו כמו קטע הקוד הבא:

// Uglified JavaScript production code:
export function injectScript(){const t=document.createElement("script");t.src="/js/scripts.js",t.type="module",document.body.appendChild(t)}

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

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

הדגמות של JavaScript

בוחנים את הידע

מהי הדרך הטובה ביותר לטעון כמה קובצי CSS בדפדפן?

הצהרת @import של שירות ה-CSS.
מספר רכיבי <link>.

מה עושה סורק הטעינה מראש של הדפדפן?

זיהוי רכיבי <link rel="preload"> בתוך משאב HTML.
זהו מנתח HTML משני שבוחן את תגי העיצוב הגולמיים כדי לגלות משאבים לפני שמנתח ה-DOM יכול לגלות אותם מוקדם יותר.

מדוע הדפדפן חוסם באופן זמני את ניתוח ה-HTML כברירת מחדל כאשר מוריד את משאבי JavaScript?

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

השלב הבא: סיוע לדפדפן באמצעות רמזים לגבי משאבים

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