פיצול קוד עם ייבוא דינמי ב-Next.js

איך אפשר להאיץ את האפליקציה ב-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 אוסף את האפליקציה, בודקים את מעקב הפעילות ברשת בכלים למפתחים:

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

  2. מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את DevTools.

  3. לוחצים על הכרטיסייה רשתות.

  4. מסמנים את התיבה Disable cache (השבתת המטמון).

  5. טוענים מחדש את הדף.

כשאתם טוענים את הדף, כל הקוד הנדרש, כולל הרכיב Puppy.js, נארז ב-index.js:

הכרטיסייה 'רשת' בכלי הפיתוח, שבה מוצגים שישה קובצי JavaScript: index.js,‏ app.js,‏ webpack.js,‏ main.js,‏ 0.js וקובץ ה-dll (ספריית קישור דינמי).

כשלוחצים על הלחצן Click me, רק הבקשה לקובץ ה-JPEG של הגור מתווספת לכרטיסייה Network:

הכרטיסייה 'רשת' ב-DevTools אחרי לחיצה על הלחצן, שבה מוצגים אותם שישה קובצי JavaScript ותמונה אחת.

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

עכשיו נבדוק גרסה שנייה של האפליקציה, שבה הייבוא הסטטי הוחלף בייבוא דינמי. ב-Next.js יש את next/dynamic, שמאפשר להשתמש בייבוא דינמי לכל הרכיבים ב-Next:

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

פועלים לפי השלבים בדוגמה הראשונה כדי לבדוק את מעקב הנתונים ברשת.

כשאתם טוענים את האפליקציה בפעם הראשונה, רק index.js מורדת. הפעם הוא קטן ב-0.5KB (הוא ירד מ-37.9KB ל-37.4KB) כי הוא לא כולל את הקוד של הרכיב Puppy:

DevTools Network שמוצגים בו אותם שישה קובצי JavaScript, מלבד index.js שקטן עכשיו ב-0.5KB.

הרכיב Puppy נמצא עכשיו בחלק נפרד, 1.js, שנטען רק כשמקישים על הלחצן:

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

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

ייבוא דינמי עם אינדיקטור טעינה מותאם אישית

כשאתם מעמיסים משאבים באיטרציה, מומלץ לספק אינדיקטור טעינה למקרה שיחולו עיכובים. ב-Next.js, אפשר לעשות זאת על ידי הוספת ארגומנט נוסף לפונקציה dynamic():

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading: () => <p>Loading...</p>
});

כדי לראות את אינדיקטור הטעינה בפעולה, אפשר לדמות חיבור איטי לרשת ב-DevTools:

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

  2. מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את DevTools.

  3. לוחצים על הכרטיסייה רשתות.

  4. מסמנים את התיבה Disable cache (השבתת המטמון).

  5. ברשימה הנפתחת Throttling בוחרים באפשרות Fast 3G.

  6. לוחצים על הלחצן Click me.

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

מסך כהה עם הטקסט

ייבוא דינמי ללא SSR

אם אתם צריכים להציג רכיב רק בצד הלקוח (לדוגמה, ווידג'ט צ'אט), תוכלו לעשות זאת על ידי הגדרת האפשרות ssr לערך false:

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr: false,
});

סיכום

התמיכה בייבוא דינמי מאפשרת ל-Next.js לבצע פיצול קוד ברמת הרכיב, וכך לצמצם את המטענים הייעודיים (payloads) של JavaScript ולקצר את זמני הטעינה של האפליקציה. כברירת מחדל, כל הרכיבים עוברים עיבוד בצד השרת, ואפשר להשבית את האפשרות הזו בכל שלב.