כדי לשפר את הביצועים של האפליקציה, כדאי להשתמש בפיצול קוד ברמת המסלול.
במאמר הזה נסביר איך להגדיר פיצול קוד ברמת המסלול באפליקציית Angular. הפעולה הזו יכולה לצמצם את גודל החבילה של JavaScript ולשפר באופן משמעותי את זמן הטעינה עד לאפשרות האינטראקציה.
דוגמאות הקוד מהמאמר הזה זמינות ב-GitHub. הדוגמה לניתוב מיידי זמינה בהענף eager. הדוגמה לפיצול קוד ברמת המסלול נמצאת בהסתעפות העצלנית.
למה חשוב לפצל את הקוד
המורכבות ההולכת וגדלה של אפליקציות אינטרנט הובילה לעלייה משמעותית בכמות ה-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 {}
פיצול קוד ברמת המסלול
כדי להגדיר חלוקת קוד, צריך לבצע רפאקציה (refactoring) של המסלול המהיר 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)
}
יש שני הבדלים עיקריים מהנתיב המהיר:
- הגדרתם את
loadChildren
במקום אתcomponent
. כשמשתמשים בפיצול קוד ברמת המסלול, צריך להפנות למודולים שנטענים באופן דינמי במקום לרכיבים. - ב-
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
באופן עצלני בסביבה המקומית:
- מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את DevTools.
לוחצים על הכרטיסייה רשתות.
לוחצים על NYAN באפליקציה לדוגמה.
שימו לב שקובץ
nyan-nyan-module.js
מופיע בכרטיסייה 'רשת'.
הדוגמה הזו זמינה ב-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, אפשר להשתמש בפיצול קוד ברמת המסלול:
- שימוש ב-Angular CLI ליצירת מודולים טעונים באיטרציה (lazy-loaded) כדי ליצור באופן אוטומטי תבנית של מסלול שנטען באופן דינמי.
- מוסיפים אינדיקטור טעינה כשהמשתמש מנווט למסלול עצל כדי להראות שיש פעולה מתמשכת.