איך להאיץ את אפליקציית Next.js באמצעות פיצול קוד ואסטרטגיות טעינה חכמות.
מה תלמדו?
בפוסט הזה מוסברים סוגים שונים של קוד פיצול ואיך משתמשים ייבוא דינמי להאצה של אפליקציות Next.js.
פיצול קוד שמבוסס על מסלול ומבוסס על רכיבים
כברירת מחדל, Next.js מפצל את קוד ה-JavaScript למקטעי נתונים נפרדים עבור כל נתיב. כשמשתמשים טוענים את האפליקציה שלך, Next.js שולח רק את הקוד הדרוש במסלול הראשוני. כשמשתמשים מנווטים באפליקציה, הם מאחזרים את המקטעים שמשויכים למסלולים האחרים. פיצול קוד מבוסס מסלול מפחית את כמות הסקריפטים שצריך לנתח ולהרכיב בו זמנית, וכתוצאה מכך זמני טעינה מהירים יותר של דפים.
פיצול קוד מבוסס מסלול הוא ברירת מחדל טובה, אבל אפשר לבצע אופטימיזציה נוספת תהליך טעינה עם פיצול קוד ברמת הרכיב. אם יש לכם רכיבים באפליקציה, כדאי לפצל אותם למקטעי נתונים נפרדים. באופן כזה, רכיבים גדולים שאינם קריטיים או מעובדים רק בחלקים מסוימים ניתן לטעון באופן מדורג אינטראקציות של משתמשים (כמו לחיצה על לחצן).
Next.js תומך ב-import()
דינמי,
שמאפשר לייבא מודולים של JavaScript (כולל רכיבי React)
באופן דינמי, טוענים כל ייבוא כמקטע נפרד. הפעולה הזאת מספקת
פיצול קוד ברמת הרכיב ומאפשר לכם לשלוט בטעינת המשאבים
שמשתמשים מורידים רק את הקוד שהם צריכים עבור החלק באתר
שהם צופים. ב-Next.js, הרכיבים האלה עוברים רינדור בצד השרת
(SSR)
כברירת מחדל.
ייבוא דינמי בפעולה
הפוסט הזה כולל כמה גרסאות של אפליקציה לדוגמה, שכוללת בדף באמצעות לחצן אחד. כשלוחצים על הלחצן, רואים גור חמוד. בתור עוברים בכל אחת מגרסאות האפליקציה, רואים את הייבוא הדינמי שונה מסטטי ייבוא ואיך לעבוד איתם.
בגרסה הראשונה של האפליקציה, הכלבלב גר ב-components/Puppy.js
. שפת תרגום
להציג את הכלבלב בדף, האפליקציה מייבאת את הרכיב Puppy
index.js
עם הצהרת ייבוא סטטית:
import Puppy from "../components/Puppy";
כדי לראות איך Next.js מקבץ את האפליקציה, צריך לבדוק את מעקב הרשת ב'כלי פיתוח':
כדי לראות תצוגה מקדימה של האתר, לוחצים על הצגת האפליקציה. לאחר מכן לוחצים על מסך מלא
.
מקישים על 'Control+Shift+J' (או על 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח.
לוחצים על הכרטיסייה רשתות.
מסמנים את התיבה Disable cache (השבתת מטמון).
לטעון מחדש את הדף.
בזמן הטעינה של הדף, כל הקוד הדרוש, כולל Puppy.js
רכיב, נכלל בחבילה ב-index.js
:
![הכרטיסייה 'רשת כלי הפיתוח' שמוצגים בה שישה קובצי JavaScript: index.js, app.js, webpack.js, main.js, 0.js וקובץ dll (ספריית קישורים דינמיים)](https://web.dev/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/devtools-network-tab-show-c3449cc648185.png?authuser=5&hl=he)
כשלוחצים על הלחצן Click me (לוחצים עליי), רק הבקשה עבור puppy JPEG נוספו לכרטיסייה רשת:
![הכרטיסייה 'רשת כלי הפיתוח' אחרי לחיצה על הלחצן, שמוצגים בה אותם שישה קובצי JavaScript ותמונה אחת.](https://web.dev/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/devtools-network-tab-the-94b209c5e4e56.png?authuser=5&hl=he)
החיסרון של גישה זו הוא שגם אם המשתמשים לא לוחצים על הלחצן
לראות את הכלבלב, הוא צריך לטעון את הרכיב Puppy
כי הוא כלול
index.js
. בדוגמה הקטנה הזאת, זה לא הרבה, אבל בעולם האמיתי
לפעמים מדובר בשיפור משמעותי כשטוענים רכיבים גדולים רק
הנחוצים.
עכשיו בדוק את הגרסה השנייה של האפליקציה, שבה הייבוא הסטטי
הוחלף בייבוא דינמי. Next.js כולל את next/dynamic
, מה שהופך אותו
להשתמש בייבוא דינמי של כל הרכיבים שבשדה 'הבא':
import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";
// ...
const Puppy = dynamic(import("../components/Puppy"));
פועלים לפי השלבים מהדוגמה הראשונה כדי לבדוק את מעקב הרשת.
בטעינה הראשונה של האפליקציה, תתבצע הורדה של רק index.js
. הפעם היא
קטן יותר ב-0.5 KB (הוא ירד מ-37.9 KB ל-37.4 KB) מפני
לא כולל את הקוד של הרכיב Puppy
:
![רשת DevTools מציגה את אותם שישה קובצי JavaScript, מלבד index.js קטן יותר ב-0.5KB.](https://web.dev/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/devtools-network-showing-8cbd4309d29c5.png?authuser=5&hl=he)
הרכיב Puppy
נמצא עכשיו במקטע נפרד, 1.js
, שנטען רק
כשלוחצים על הלחצן:
![הכרטיסייה 'רשת כלי הפיתוח' שאחרי הלחיצה על הלחצן, שבה מוצגים קובץ 1.js הנוסף והתמונה שנוספה לתחתית רשימת הקבצים.](https://web.dev/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/devtools-network-tab-the-e42cc1846b47f.png?authuser=5&hl=he)
באפליקציות בעולם האמיתי, רכיבים הם לרוב גדול יותר, וטעינה מדורגת יכול לחתוך את המטען הייעודי (payload) הראשוני של JavaScript במאות קילובייט.
ייבוא דינמי עם אינדיקטור טעינה מותאם אישית
כשמבצעים טעינה מדורגת של משאבים, מומלץ לספק אינדיקטור לטעינה
למקרה שיהיו עיכובים. ב-Next.js, אפשר לעשות זאת באמצעות
ארגומנט נוסף לפונקציה dynamic()
:
const Puppy = dynamic(() => import("../components/Puppy"), {
loading: () => <p>Loading...</p>
});
כדי לראות את אינדיקטור הטעינה בפעולה, צריך לבצע סימולציה של חיבור איטי לרשת כלי פיתוח:
כדי לראות תצוגה מקדימה של האתר, לוחצים על הצגת האפליקציה. לאחר מכן לוחצים על מסך מלא
.
מקישים על 'Control+Shift+J' (או על 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח.
לוחצים על הכרטיסייה רשתות.
מסמנים את התיבה Disable cache (השבתת מטמון).
ברשימה הנפתחת Throttling, בוחרים באפשרות Fast 3G.
לוחצים על הלחצן Click me (אני רוצה ללחוץ).
עכשיו, כשלוחצים על הלחצן, לוקח קצת זמן לטעון את הרכיב ואת האפליקציה מציג את ההודעה "טוען..." בינתיים.
![מסך כהה עם הטקסט](https://web.dev/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/a-dark-screen-the-text-914039a8f2589.png?authuser=5&hl=he)
ייבוא דינמי ללא SSR
אם אתם צריכים לעבד רכיב רק בצד הלקוח (לדוגמה, צ'אט)
ווידג'ט) אפשר לעשות זאת על ידי הגדרת האפשרות ssr
ל-false
:
const Puppy = dynamic(() => import("../components/Puppy"), {
ssr: false,
});
סיכום
עם תמיכה בייבוא דינמי, Next.js מספק קוד ברמת הרכיב פיצול, שיכול למזער את מטענים ייעודיים (payloads) של JavaScript ולשפר את האפליקציה זמן הטעינה. כל הרכיבים עוברים עיבוד בצד השרת כברירת מחדל, וניתן להשבית את האפשרות הזו במקרה הצורך.