לטעינת משאבי JavaScript גדולים יש השפעה משמעותית על מהירות הדף. פיצול את JavaScript למקטעי נתונים קטנים יותר ומורידים רק את מה שדרוש דף שיפעל במהלך ההפעלה יכול לשפר באופן משמעותי את טעינת הדף רספונסיביות, שבתורה יכולה לשפר את האינטראקציה בדף הבא Paint (INP).
כשדף מוריד, מנתח ומהדר קובצי JavaScript גדולים, הוא יכול להפוך חוסר תגובה למשך פרקי זמן מסוימים. רכיבי הדף גלויים, מפני שהם חלק מה-HTML הראשוני של הדף, והסגנון מעוצב על ידי CSS. אבל מכיוון שה-JavaScript שנדרש כדי להפעיל את הרכיבים האינטראקטיביים האלה, וגם סקריפטים אחרים שנטענים על ידי ייתכן שהדף מנתח ומפעיל את ה-JavaScript כדי לפעול באופן תקין. כתוצאה מכך, המשתמש עשוי להרגיש שהאינטראקציה הייתה משמעותית מתעכבות, או אפילו מנותקות לגמרי.
לרוב זה קורה כי ה-thread הראשי חסום במהלך ניתוח של JavaScript וקובצו ב-thread הראשי. אם התהליך נמשך זמן רב מדי, ייתכן שרכיבי הדף לא מגיבים מהר מספיק לקלט של משתמשים. אחד הפתרונות לבעיה הוא לטעון רק את קוד ה-JavaScript שדרוש כדי שהדף יפעל, בעוד דחיית טעינה של JavaScript אחר בשלב מאוחר יותר באמצעות שיטה שנקראת קוד של פיצול נתונים. יחידת הלימוד הזו מתמקדת בשני הטכניקות האלה.
הפחתת הניתוח וההפעלה של JavaScript במהלך ההפעלה באמצעות פיצול קוד
התג Lighthouse מציג אזהרה כשהפעלת ה-JavaScript נמשכת יותר מ-2 שניות, והוא נכשל אחרי יותר מ-3.5 שניות. כמות גדולה מדי של JavaScript ניתוח והפעלה הם בעיה אפשרית בכל נקודה בדף במחזור החיים, כי יש לו פוטנציאל להאריך את העיכוב בקלט של אינטראקציה אם הזמן שבו המשתמש יוצר אינטראקציה עם הדף חופף לרגע משימות ה-thread הראשיות שאחראיות לעיבוד ולביצוע של JavaScript הן ריצה.
מעבר לזה, יש צורך בכמות מוגזמת של הפעלה וניתוח של JavaScript בעייתית במהלך הטעינה הראשונית של הדף, מכיוון שזו הנקודה בדף שיש סבירות גבוהה לכך שמשתמשים יקיימו אינטראקציה עם הדף. למעשה, זמן החסימה הכולל (TBT) – מדד של מהירות התגובה לטעינה – יש מתאם גבוה עם INP, שמרמז על כך שיש למשתמשים נטייה גבוהה לנסות אינטראקציות במהלך הטעינה הראשונית של הדף.
ביקורת של Lighthouse שמדווחת על הזמן שהוקדש לביצוע כל קובץ JavaScript שהבקשות לדפים מועילה כי היא יכולה לעזור לכם לזהות בדיוק סקריפטים עשויים להיות מועמדים לפיצול קוד. לאחר מכן תוכלו להמשיך באמצעות כלי הכיסוי שבכלי הפיתוח ל-Chrome כדי לזהות בדיוק אילו חלקים JavaScript של דף לא יהיה בשימוש במהלך טעינת הדף.
פיצול קוד היא שיטה שימושית שיכולה לצמצם את קוד ה-JavaScript הראשוני של דף מטענים ייעודיים (payloads). היא מאפשרת לפצל חבילת JavaScript לשני חלקים:
- את קוד ה-JavaScript נדרש בזמן טעינת הדף, ולכן לא ניתן לטעון אותו באף תבנית אחרת בזמן האימון.
- שאר קוד ה-JavaScript שניתן לטעון במועד מאוחר יותר, בדרך כלל בנקודה שבה המשתמש יוצר אינטראקציה עם רכיב אינטראקטיבי נתון הדף.
אפשר לפצל את הקוד באמצעות תחביר import()
דינמי. הזה
התחביר - בשונה מרכיבי <script>
שמבקשים משאב JavaScript נתון
במהלך ההפעלה — שולח בקשה למשאב JavaScript בשלב מאוחר יותר במהלך
במחזור החיים של הדף.
document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
// Get the form validation named export from the module through destructuring:
const { validateForm } = await import('/validate-form.mjs');
// Validate the form:
validateForm();
}, { once: true });
בקטע ה-JavaScript הקודם, המודול validate-form.mjs
נתונים שמורידים, מנותחים ומופעלים רק כשמשתמש מטשטש טופס כלשהו
<input>
שדות. במצב כזה, משאב ה-JavaScript שאחראי על
לוגיקת האימות של הטופס תהיה תמיד מעורבת בדף רק כאשר
הכי סביר שהם ישתמשו בהן.
רכיבי Bundler של JavaScript כמו webpack, Parcel, Rollup ו-esbuild יכולים להיות
מוגדרת לפצל חבילות JavaScript למקטעי נתונים קטנים יותר בכל פעם
ייתקלו בקריאה דינמית import()
בקוד המקור שלכם. רוב הכלים האלה
באופן אוטומטי, אבל פיתוח esbuild מצריך מכם להסכים
אופטימיזציה שלו.
הערות מועילות לגבי פיצול קוד
פיצול הקוד הוא שיטה יעילה לצמצום התחרות על השרשורים הראשיים במהלך הטעינה הראשונית של הדף, כדאי לזכור כמה דברים אם תחליטו לבדוק את קוד המקור של JavaScript כדי לאתר הזדמנויות לפיצול קוד.
אם אפשר, כדאי להשתמש ב-bundler
מפתחים רבים נוהגים להשתמש במודולים של JavaScript או בלתי מונחית. זה שיפור מצוין של חוויית המפתח משפרת את הקריאוּת והתחזוקה של הקוד. אבל יש כמה מודלים מאפייני ביצועים לא אופטימליים שעלולים להיגרם בעת שליחת JavaScript של מודלים בסביבת ייצור.
הדבר החשוב ביותר הוא להשתמש ב-bundler כדי לעבד את המקור ולבצע אופטימיזציה שלו כולל מודולים שאתם מתכוונים לפצל את הקוד. ביצים יעילים מאוד לא רק החלת אופטימיזציות לקוד המקור של JavaScript, אלא גם יעיל למדי באיזון שיקולי ביצועים כמו גודל החבילה מול יחס הדחיסה. יעילות הדחיסה עולה ככל שהחבילה גדולה יותר, אבל החבילות מנסים גם לוודא שהחבילות לא גדולות כל כך משימות ארוכות עקב הערכת סקריפטים.
יצרני חבילה מונעים גם את הבעיה של משלוח מספר גדול של מודולים לא ארוזים
דרך הרשת. לארכיטקטורות שמשתמשות במודולים של JavaScript יש בדרך כלל
עצי מודול מורכבים. כשעצי מודול לא מקובצים, כל מודול מייצג
בקשת HTTP נפרדת, והאינטראקטיביות באפליקציית האינטרנט עשויה להתעכב אם
הם לא מקבצי מודולים. אומנם אפשר להשתמש
רמז על משאב אחד (<link rel="modulepreload">
) לטעינת עצי מודול גדולים כבר בשלב מוקדם
עד כמה שאפשר, עדיין עדיף להשתמש בחבילות JavaScript מאשר בביצועי הטעינה
נקודת מבט.
לא להשבית בטעות הידור של סטרימינג
מנוע V8 של JavaScript ב-Chromium מציע מספר אופטימיזציות ייחודיות כדי להבטיח שקוד ה-JavaScript בסביבת הייצור נטען באופן יעיל ככל האפשר. אחת מהאופטימיזציות האלה נקראת אוסף של תכנים בסטרימינג, למשל ניתוח מצטבר של HTML שעובר בסטרימינג לדפדפן - הידור מקטעי נתונים בסטרימינג JavaScript כאשר הם מגיעים מהרשת.
יש כמה דרכים להבטיח שהאוסף של הסטרימינג יתבצע עבור אפליקציית אינטרנט ב-Chromium:
- לשנות את קוד הייצור כדי להימנע משימוש במודולים של JavaScript. ברנדרים יכול לשנות את קוד המקור של JavaScript על סמך יעד הידור, היעד הוא בדרך כלל ספציפי לסביבה מסוימת. V8 יפעיל סטרימינג מ-JavaScript לכל קוד JavaScript שלא משתמש במודולים, להגדיר את ה-bundler כדי להפוך את הקוד של מודול ה-JavaScript לתחביר שלא משתמש במודולים של JavaScript ובתכונות שלהם.
- כדי לשלוח מודולים של JavaScript לסביבת הייצור, צריך להשתמש ב
.mjs
לתוסף. גם אם גרסת ה-JavaScript בסביבת הייצור משתמשת במודולים, יש אין סוג תוכן מיוחד ל-JavaScript המשתמש במודולים לעומת JavaScript אחרת. כאשר מדובר ב-V8, אתם למעשה מבטלים את ההסכמה לסטרימינג שנאספים כששולחים מודולים של JavaScript בסביבת ייצור באמצעות.js
. לתוסף. אם משתמשים בתוסף.mjs
למודולים של JavaScript, V8 יכול מוודאים שהאוסף של סטרימינג באמצעות קוד JavaScript מבוסס-מודול לא שבור.
אל תתנו לשיקולים האלה למנוע מכם להשתמש בפיצול קוד. קוד פגישה פיצול הוא דרך יעילה לצמצום מטענים ייעודיים (payloads) ראשוניים של JavaScript למשתמשים, אבל על ידי שימוש ב-bundler והכרת האופן שבו אפשר לשמר את הסטרימינג של V8 של compilation, ניתן לוודא שקוד ה-JavaScript בסביבת הייצור מהר למשתמשים.
הדגמה של ייבוא דינמי
חבילה ו-Webpack
webpack מגיע עם פלאגין בשם SplitChunksPlugin
, שמאפשר לכם
להגדיר את האופן שבו ה-bundler מפצל קובצי JavaScript. ה-webpack מזהה גם את
הצהרות import()
דינמיות ו-import
סטטיות. ההתנהגות של
אפשר לשנות את SplitChunksPlugin
על ידי ציון האפשרות chunks
תצורה:
chunks: async
הוא ערך ברירת המחדל, והוא מתייחס לקריאות דינמיות שלimport()
.chunks: initial
מתייחס להפעלותimport
סטטיות.chunks: all
מכסה גם ייבוא דינמי שלimport()
וגם ייבוא סטטי, מה שמאפשר לך כדי לשתף מקטעים בין ייבוא שלasync
ל-initial
.
כברירת מחדל, בכל פעם ש-webpack נתקל בהצהרה דינמית של import()
. זה
יוצרת מקטע נפרד למודול הזה:
/* main.js */
// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';
myFunction('Hello world!');
// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
// Assumes top-level await is available. More info:
// https://v8.dev/features/top-level-await
await import('/form-validation.js');
}
תצורת ברירת המחדל של חבילת האינטרנט עבור קטע הקוד הקודם מובילה לשתי אפשרויות: מקטעים נפרדים:
- המקטע
main.js
(ש-webpack מסווג כמקטעinitial
) כולל את המודולmain.js
ו-./my-function.js
. - המקטע
async
, שכולל רקform-validation.js
(שמכיל רק גיבוב של קובץ בשם המשאב, אם הוגדר). ניתן להוריד את המקטע הזה בלבד אם וכאשרcondition
הוא נכון.
ההגדרה הזו מאפשרת לדחות את הטעינה של המקטע form-validation.js
עד
זה אכן נחוץ. הפעולה הזו יכולה לשפר את מהירות התגובה של הטעינה על ידי הפחתת הסקריפט
הערכה במהלך הטעינה הראשונית של הדף. הורדה והערכה של סקריפטים
עבור המקטע form-validation.js
מתרחש כשתנאי שצוין מתקיים,
במקרה כזה, תתבצע הורדה של המודול שמיובא באופן דינמי. למשל,
תנאי שבו מורידים polyfill רק לדפדפן מסוים, או כמו
מהדוגמה הקודמת – המודול המיובא הכרחי לאינטראקציה של משתמש.
לעומת זאת, שינוי ההגדרות של SplitChunksPlugin
כדי לציין
הפונקציה chunks: initial
מבטיחה שהקוד מפוצל רק במקטעי נתונים ראשוניים. הנושאים האלה
מקטעים כמו אלה שיובאו באופן סטטי או רשומים ב-entry
של ה-webpack
לנכס. בהמשך לדוגמה שלמעלה, המקטע שיתקבל יהיה
שילוב של form-validation.js
ו- main.js
בקובץ סקריפט יחיד,
מה שעלול להוביל לירידה בביצועים של טעינת הדף הראשונית.
אפשר גם להגדיר את האפשרויות של SplitChunksPlugin
להפריד בין פריטים גדולים יותר
סקריפטים מתחלקים לכמה סקריפטים קטנים יותר. לדוגמה, באמצעות האפשרות maxSize
.
להנחות את ה-webpack לפצל מקטעים לקבצים נפרדים אם הם חורגים
צוין על ידי maxSize
. חלוקת קובצי סקריפטים גדולים לקבצים קטנים יותר עשויה
לשפר את מהירות התגובה לעומסים, כמו במקרים מסוימים הערכת סקריפטים שצורכים הרבה מעבד (CPU)
העבודה מחולקת למשימות קטנות יותר, שיש סיכוי נמוך יותר שחוסמות את
לפרקי זמן ארוכים יותר.
בנוסף, המשמעות של יצירת קובצי JavaScript גדולים היא שהסקריפטים גבוהה יותר במצב של ביטול תוקף של מטמון. לדוגמה, אם אתם שולחים סקריפט גדול עם framework וקוד של אפליקציה של צד ראשון, כל ניתן לבטל את תוקף החבילה אם רק ה-framework יעודכן, אבל לא שום דבר נוסף משאב בחבילה.
מצד שני, קובצי סקריפט קטנים יותר מגדילים את הסבירות המבקר מאחזר משאבים מהמטמון, וכתוצאה מכך הדפים נטענים מהר יותר וביקורים חוזרים. עם זאת, דחיסת נתונים קטנה יותר מאשר לקבצים גדולים יותר האלה, ועשויה להאריך את זמן הלוך ושוב ברשת בטעינות דפים באמצעות מטמון הדפדפן. חשוב לשמור על איזון בין שמירה במטמון יעילות, יעילות דחיסה וזמן הערכת סקריפט.
הדגמה של Webpack
הדגמה של SplitChunksPlugin
webpack.
בוחנים את הידע
איזה סוג של הצהרה import
משמש לביצוע קוד
לפצל?
import
סטטי.import()
דינמי.
איזה סוג של משפט import
חייב להופיע בחלק העליון של הדף
של מודול JavaScript ולא במיקום אחר?
import
סטטי.import()
דינמי.
כשמשתמשים ב-SplitChunksPlugin
ב-webpack, מהו
את ההבדל בין מקטע async
מקטע initial
?
async
מקטעים נטענים באמצעות import
סטטי
ו-initial
מקטעים נטענים באמצעות
import()
async
מקטעים נטענים באמצעות import()
דינמי
ו-initial
מקטעים נטענים באמצעות סטטיות
import
הסרטון הבא: טעינה מדורגת של תמונות ורכיבי <iframe>
למרות שבדרך כלל מדובר בסוג משאב יקר למדי, JavaScript אינו
רק את סוג המשאב שאפשר לדחות את הטעינה שלו. תמונה ורכיבי <iframe>
הם משאבים יקרים כשלעצמם. בדומה ל-JavaScript, עליך
יכול לדחות את הטעינה של תמונות ורכיב <iframe>
על ידי טעינה מדורגת
, כפי שמוסבר במודול הבא בקורס.