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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  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 אלפיות שנייה כדי שתוכלו לראות את הספינר בפעולה.

סיכום

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

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