במודול הקודם, הסברנו את התיאוריה שמאחורי נתיב העיבוד הקריטי, ואיך משאבים שחוסמים את העיבוד וחוסמים את הניתוח יכולים לעכב את העיבוד הראשוני של הדף. אחרי שהבנתם את הרקע התיאורטי, אתם מוכנים ללמוד כמה טכניקות לאופטימיזציה של נתיב העיבוד הקריטי.
במהלך טעינת דף, יש הפניות למשאבים רבים בקוד ה-HTML שלו. המשאבים האלה מספקים לדף את המראה והפריסה שלו באמצעות CSS, וגם את האינטראקטיביות שלו באמצעות JavaScript. במודול הזה נסביר כמה מושגים חשובים שקשורים למשאבים האלה ולאופן שבו הם משפיעים על זמן הטעינה של דף.
חסימת עיבוד
כמו שצוין במודול הקודם, CSS הוא משאב שחוסם את העיבוד, כי הוא מונע מהדפדפן לעבד תוכן כלשהו עד שנוצר מודל אובייקט 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 – או type=module שהוא defer כברירת מחדל), המנתח נחסם מהרגע שמתגלה הקובץ ועד שהוא מורד, מנותח ומופעל. כשמשתמשים ב-JavaScript מוטבע, מנתח התגיות נחסם באופן דומה עד שהסקריפט המוטבע מנותח ומופעל.
סורק הטעינה מראש
סורק הטעינה מראש הוא אופטימיזציה של הדפדפן בצורה של מנתח HTML משני, שסורק את תגובת ה-HTML הגולמית כדי למצוא משאבים ולאחזר אותם באופן ספקולטיבי לפני שמנתח ה-HTML הראשי יגלה אותם. לדוגמה, סורק הטעינה מראש יאפשר לדפדפן להתחיל להוריד משאב שצוין ברכיב <img>, גם אם מנתח ה-HTML חסום בזמן אחזור ועיבוד של משאבים כמו CSS ו-JavaScript.
כדי להשתמש בסורק הטעינה מראש, צריך לכלול משאבים קריטיים בתגי העיצוב של ה-HTML שנשלחים מהשרת. הסורק של טעינה מראש לא יכול לזהות את דפוסי טעינת המשאבים הבאים:
- תמונות שנטענו על ידי CSS באמצעות המאפיין
background-image. הפניות לתמונות האלה נמצאות ב-CSS, וסורק הטעינה מראש לא יכול לגלות אותן. - סקריפטים שנטענים באופן דינמי בצורה של תגי
<script>markup שמוחדרים ל-DOM באמצעות JavaScript או מודולים שנטענים באמצעות dynamicimport(). - HTML שעבר עיבוד בצד הלקוח באמצעות JavaScript. תגי העיצוב האלה כלולים במחרוזות במשאבי JavaScript, ולא ניתן לגלות אותם באמצעות סורק הטעינה מראש.
- הצהרות של שירות CSS
@import.
כל דפוסי טעינת המשאבים האלה הם משאבים שמתגלים בשלב מאוחר, ולכן הם לא נהנים מהסורק של טעינה מראש. כדאי להימנע מהם ככל האפשר. אם אי אפשר להימנע מדפוסים כאלה, אפשר להשתמש ברמז 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 במקרים מסוימים. כלים כמו bundlers יכולים לבצע את האופטימיזציה הזו באופן אוטומטי בגרסאות build של ייצור.
הסרה של CSS שלא בשימוש
לפני עיבוד התוכן, הדפדפן צריך להוריד ולנתח את כל גיליונות הסגנונות. הזמן שנדרש להשלמת הניתוח כולל גם סגנונות שלא נעשה בהם שימוש בדף הנוכחי. אם אתם משתמשים בחבילת קבצים שמשלבת את כל משאבי ה-CSS בקובץ אחד, סביר להניח שהמשתמשים שלכם מורידים יותר CSS ממה שנדרש כדי לעבד את הדף הנוכחי.
כדי לגלות תוכן CSS שלא נעשה בו שימוש בדף הנוכחי, משתמשים בכלי Coverage ב-Chrome DevTools.
להסרת רכיבי CSS שאינם בשימוש יש השפעה כפולה: בנוסף לקיצור זמן ההורדה, מתבצעת אופטימיזציה של בניית עץ העיבוד, כי הדפדפן צריך לעבד פחות כללי CSS.
הימנעות מהצהרות CSS @import
למרות שזה אולי נראה נוח, כדאי להימנע מהצהרות @import ב-CSS:
/* Don't do this: */
@import url('style.css');
בדומה לאופן הפעולה של הרכיב <link> ב-HTML, ההצהרה @import ב-CSS מאפשרת לייבא משאב CSS חיצוני מתוך גיליון סגנונות. ההבדל העיקרי בין שתי הגישות האלה הוא שהרכיב <link> HTML הוא חלק מתגובת ה-HTML, ולכן הוא מתגלה הרבה יותר מוקדם מקובץ CSS שהורד על ידי הצהרת @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 מופעלים כשהניתוח התחבירי של מסמך ה-HTML מסתיים – זה קורה באותו זמן כמו האירוע DOMContentLoaded בדפדפן.
בנוסף, יכול להיות שסקריפטים של async יפעלו לא לפי הסדר, בעוד שסקריפטים של defer יפעלו לפי הסדר שבו הם מופיעים בתגי העיצוב.
רינדור בצד הלקוח
באופן כללי, מומלץ להימנע משימוש ב-JavaScript כדי לרנדר תוכן קריטי או רכיב LCP של דף. התהליך הזה נקרא עיבוד בצד הלקוח, והוא טכניקה שנמצאת בשימוש נרחב באפליקציות של דף יחיד (SPA).
תגי עיצוב שעברו עיבוד באמצעות JavaScript לא נסרקים על ידי סורק הטעינה מראש, כי הסורק לא יכול לגלות את המשאבים שכלולים בתגי העיצוב שעברו עיבוד בצד הלקוח. הדבר עלול לעכב את ההורדה של משאבים חיוניים, כמו תמונת LCP. הדפדפן מתחיל להוריד את תמונת ה-LCP רק אחרי שהסקריפט מופעל והרכיב נוסף ל-DOM. בתמורה, אפשר להריץ את הסקריפט רק אחרי שהוא נמצא, הורד ונותח. התופעה הזו נקראת שרשור של בקשות קריטיות, ומומלץ להימנע ממנה.
בנוסף, עיבוד תגי עיצוב באמצעות JavaScript עלול ליצור משימות ארוכות בתדירות גבוהה יותר מאשר תגי עיצוב שהורדו מהשרת בתגובה לבקשת ניווט. שימוש נרחב בעיבוד HTML בצד הלקוח עלול לפגוע בחביון של האינטראקציה. זה נכון במיוחד במקרים שבהם ה-DOM של הדף גדול מאוד, מה שמפעיל עבודת רינדור משמעותית כשקוד JavaScript משנה את ה-DOM.
הקטנה
בדומה ל-CSS, הקטנה של JavaScript מצמצמת את גודל הקובץ של משאב סקריפט. התוצאה היא הורדות מהירות יותר, והדפדפן יכול לעבור מהר יותר לתהליך של ניתוח והידור של JavaScript.
בנוסף, הקטנת JavaScript היא שלב נוסף מעבר להקטנת נכסים אחרים, כמו CSS. כשמבצעים מיניפיקציה של JavaScript, לא רק שמסירים ממנו דברים כמו רווחים, טאבים ותגובות, אלא גם מקצרים סמלים ב-JavaScript של המקור. התהליך הזה נקרא לפעמים uglification. כדי לראות את ההבדל, אפשר להשתמש בקוד המקור הבא של 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);
}
כשמבצעים uglification בקוד המקור של 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 של אתר מספק.
אם אתם משתמשים בכלי לאיגוד קבצים כדי לעבד את קוד המקור של האתר, לעיתים קרובות תהליך ה-uglification מתבצע באופן אוטומטי בגרסאות שמיועדות לייצור. גם כלים להסרת רווחים, כמו Terser, ניתנים להגדרה במידה רבה, כך שאפשר לשנות את רמת האגרסיביות של האלגוריתם להסרת רווחים כדי להשיג חיסכון מקסימלי. עם זאת, הגדרות ברירת המחדל של כל כלי להסרת תכונות מיותרות בדרך כלל מספיקות כדי למצוא את האיזון הנכון בין גודל הפלט לבין שמירה על היכולות.
הדגמות של JavaScript
בוחנים את הידע
מה הדרך הכי טובה לטעון כמה קובצי CSS בדפדפן?
@import של CSS.<link>.מה עושה סורק הטעינה מראש בדפדפן?
<link rel="preload"> במשאב HTML.
למה הדפדפן חוסם באופן זמני את הניתוח של HTML כברירת מחדל כשמורידים משאבי JavaScript?
הנושא הבא: עזרה לדפדפן באמצעות רמזים למשאבים
עכשיו, אחרי שהבנתם איך משאבים שנטענים ברכיב <head> יכולים להשפיע על טעינת הדף הראשונית ועל מדדים שונים, הגיע הזמן להמשיך. במודול הבא, נסביר על רמזים למשאבים ואיך הם יכולים לספק לדפדפן רמזים חשובים להתחלת טעינת משאבים ולפתיחת חיבורים לשרתים חוצי-מקורות מוקדם יותר ממה שהדפדפן היה עושה בלעדיהם.