פיצול קוד ברמת המסלול ב-Angular

כדי לשפר את ביצועי האפליקציה, אפשר להשתמש בפיצול קוד ברמת המסלול.

בפוסט הזה נסביר איך להגדיר פיצול קוד ברמת המסלול באפליקציה Angular. ההגדרה הזו יכולה להקטין את גודל החבילה של JavaScript ולשפר משמעותית את הזמן עד לפעילות מלאה.

אפשר למצוא את דוגמאות הקוד במאמר הזה ב-GitHub. הדוגמה לניתוב eager זמינה בהסתעפות eager. הדוגמה לפיצול קוד ברמת המסלול נמצאת בהסתעפות מדורגת.

למה חשוב לפצל קוד

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

הדרך היעילה ביותר לכווץ חבילות JavaScript מבלי לוותר על תכונות באפליקציות היא להפעיל פיצול קוד אגרסיבי.

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

טכניקות לפיצול קוד

אפשר לפצל את הקוד בשתי רמות: רמת הרכיב ורמת המסלול.

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

הפוסט הזה מתמקד בהגדרה של פיצול ברמת המסלול ב-Agular.

אפליקציה לדוגמה

לפני שנתעמק בפיצול הקוד ברמת המסלול ב-Agular, נבחן אפליקציה לדוגמה:

מומלץ לבדוק את ההטמעה של המודולים של האפליקציה. בתוך AppModule הוגדרו שני מסלולים: מסלול ברירת המחדל שמשויך אל HomeComponent ומסלול nyan שמשויך אל NyanComponent:

@NgModule({
  ...
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      {
        path: '',
        component: HomeComponent,
        pathMatch: 'full'
      },
      {
        path: 'nyan',
        component: NyanComponent
      }
    ])
  ],
  ...
})
export class AppModule {}

פיצול קוד ברמת המסלול

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

גרסה 8.1.0 של Angular CLI יכולה לעשות הכול עם הפקודה הבאה:

ng g module nyan --module app --route nyan

הפעולה הזו תיצור: - מודול ניתוב חדש בשם NyanModule - מסלול בAppModule בשם nyan שיטען באופן דינמי את NyanModule - מסלול ברירת מחדל בNyanModule - רכיב בשם NyanComponent שיוצג כשהמשתמש יגיע למסלול ברירת המחדל

נבצע את השלבים האלה באופן ידני כדי שנבין טוב יותר את ההטמעה של פיצול קוד באמצעות Angular!

כשהמשתמש מנווט למסלול nyan, הנתב יעבד את הערך NyanComponent בשקע החשמל של הנתב.

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

{
  path: 'nyan',
  loadChildren: () => import('./nyan/nyan.module').then(m => m.NyanModule)
}

יש שני הבדלים עיקריים מהמסלול של eager:

  1. הגדרת loadChildren במקום component. כשמשתמשים בפיצול קוד ברמת המסלול, צריך להצביע על מודולים שנטענים באופן דינמי, במקום על רכיבים.
  2. ב-loadChildren, אחרי שההבטחה תיפתר, תקבלי את NyanModule במקום להפנות אל NyanComponent.

קטע הקוד שלמעלה מציין שכאשר המשתמש מנווט אל nyan, מערכת Angular צריכה לטעון באופן דינמי את nyan.module מהספרייה nyan ולעבד את הרכיב שמשויך למסלול ברירת המחדל שמוצהר במודול.

אפשר לשייך את נתיב ברירת המחדל לרכיב באמצעות ההצהרה הזו:

import { NgModule } from '@angular/core';
import { NyanComponent } from './nyan.component';
import { RouterModule } from '@angular/router';

@NgModule({
  declarations: [NyanComponent],
  imports: [
    RouterModule.forChild([{
      path: '',
      pathMatch: 'full',
      component: NyanComponent
    }])
  ]
})
export class NyanModule {}

הקוד הזה מעובד ל-NyanComponent כשהמשתמש מנווט אל https://example.com/nyan.

כדי לבדוק שהנתב Angular מוריד את nyan.module באופן מדורג בסביבה המקומית:

  1. מקישים על 'Control+Shift+J' (או על 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח.
  2. לוחצים על הכרטיסייה רשתות.

  3. לוחצים על NYAN באפליקציה לדוגמה.

  4. חשוב לזכור שהקובץ nyan-nyan-module.js מופיע בכרטיסייה 'רשת'.

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

אפשר לראות את הדוגמה הזו ב-GitHub.

הצגת סימן גרפי שפעולה מתבצעת

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

לשם כך, צריך קודם להוסיף תגי עיצוב למחוון בתוך הרכיב router-outlet ב-app.component.html:

<router-outlet>
  <span class="loader" *ngIf="loading"></span>
</router-outlet>

לאחר מכן צריך להוסיף מחלקה AppComponent כדי לטפל באירועי ניתוב. הכיתה loading תגדיר את הדגל true כשהיא תזהה את האירוע RouteConfigLoadStart, ותגדיר את הדגל ל-false כשהוא ישמע את האירוע RouteConfigLoadEnd.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  loading: boolean;
  constructor(router: Router) {
    this.loading = false;
    router.events.subscribe(
      (event: RouterEvent): void => {
        if (event instanceof NavigationStart) {
          this.loading = true;
        } else if (event instanceof NavigationEnd) {
          this.loading = false;
        }
      }
    );
  }
}

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

סיכום

כדי לכווץ את גודל החבילה של אפליקציות ב-Agular, אפשר להחיל פיצול קוד ברמת המסלול:

  1. שימוש במחולל המודולים עם טעינה מדורגת של Angular CLI כדי לאסוף באופן אוטומטי מסלול שנטען באופן דינמי.
  2. אפשר להוסיף אינדיקטור לטעינה כשהמשתמש מנווט למסלול איטי כדי להראות שיש פעולה מתמשכת.