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

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

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

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

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

המורכבות ההולכת וגדלה של אפליקציות אינטרנט הובילה לעלייה משמעותית בכמות ה-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 {}

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

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

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

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

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

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

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

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