לדלג לתוכן

פיצול קוד בריאקט - Code Splitting מוסבר

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

הבעיה - חבילה אחת ענקית

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

הפתרון - לפצל ולהוריד לפי הצורך

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

import("./HeavyChart").then((module) => {
  const HeavyChart = module.default;
});

בריאקט, הדרך הנוחה לעבוד עם זה היא React.lazy, שעוטף את ה-import הדינמי ומאפשר להשתמש ברכיב בדיוק כמו ברכיב רגיל:

const HeavyChart = React.lazy(() => import("./HeavyChart"));

function Dashboard() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <HeavyChart />
    </Suspense>
  );
}

Suspense הוא הרכיב שמגדיר מה להציג בזמן שהקוד של HeavyChart עדיין נטען ברקע. ברגע שהקובץ מגיע, ריאקט מחליף את ה-fallback ברכיב האמיתי.

פיצול לפי נתיב - הצורה הכי נפוצה

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

const SettingsPage = React.lazy(() => import("./pages/Settings"));
const ReportsPage = React.lazy(() => import("./pages/Reports"));

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

המחיר - צריך מסך המתנה

פיצול קוד לא מגיע בחינם. כל חלק שנטען בעצלתיים דורש רגע המתנה בזמן שהדפדפן מוריד אותו, ואי אפשר פשוט להראות מסך ריק באותו רגע. כאן נכנס ה-fallback של Suspense, שיכול להיות ספינר פשוט, או תבנית שלד שממלאת מראש את המקום של התוכן שעומד להגיע. הבחירה בין השניים היא שיקול UX בפני עצמו, אבל העיקרון החשוב הוא לזכור שכל React.lazy צריך מחשבה על מה המשתמש רואה בזמן ההמתנה, ולא רק על זה שהקוד מתפצל נכון מאחורי הקלעים.

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

לסיכום

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

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

הצטרפו לקהילה בדיסקורד