完成上面的里程碑后,應(yīng)用程序很自然地長(zhǎng)大了。在某一個(gè)時(shí)間點(diǎn),你將達(dá)到一個(gè)頂點(diǎn),應(yīng)用將會(huì)需要過(guò)多的時(shí)間來(lái)加載。
為了解決這個(gè)問(wèn)題,請(qǐng)使用異步路由,它會(huì)根據(jù)請(qǐng)求來(lái)惰性加載某些特性模塊。惰性加載有很多好處。
你已經(jīng)完成了一部分。通過(guò)把應(yīng)用組織成一些模塊:?AppModule
?、?HeroesModule
?、?AdminModule
?和 ?CrisisCenterModule
?,你已經(jīng)有了可用于實(shí)現(xiàn)惰性加載的候選者。
有些模塊(比如 ?AppModule
?)必須在啟動(dòng)時(shí)加載,但其它的都可以而且應(yīng)該惰性加載。比如 ?AdminModule
?就只有少數(shù)已認(rèn)證的用戶才需要它,所以你應(yīng)該只有在正確的人請(qǐng)求它時(shí)才加載。
把 ?admin-routing.module.ts
? 中的 ?admin
?路徑從 ?'admin'
? 改為空路徑 ?''
?。
可以用空路徑路由來(lái)對(duì)路由進(jìn)行分組,而不用往 URL 中添加額外的路徑片段。用戶仍舊訪問(wèn) ?/admin
?,并且 ?AdminComponent
?仍然作為用來(lái)包含子路由的路由組件。
打開(kāi) ?AppRoutingModule
?,并把一個(gè)新的 ?admin
?路由添加到它的 ?appRoutes
?數(shù)組中。
給它一個(gè) ?loadChildren
?屬性(替換掉 ?children
?屬性)。?loadChildren
?屬性接收一個(gè)函數(shù),該函數(shù)使用瀏覽器內(nèi)置的動(dòng)態(tài)導(dǎo)入語(yǔ)法 ?import('...')
? 來(lái)惰性加載代碼,并返回一個(gè)承諾(Promise)。其路徑是 ?AdminModule
?的位置(相對(duì)于應(yīng)用的根目錄)。當(dāng)代碼請(qǐng)求并加載完畢后,這個(gè) ?Promise
?就會(huì)解析成一個(gè)包含 ?NgModule
?的對(duì)象,也就是 ?AdminModule
?。
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
},
注意:
當(dāng)使用絕對(duì)路徑時(shí),?NgModule
?的文件位置必須以 ?src/app
? 開(kāi)頭,以便正確解析。對(duì)于自定義的 使用絕對(duì)路徑的路徑映射表,你必須在項(xiàng)目的 ?tsconfig.json
? 中必須配置好 ?baseUrl
?和 ?paths
?屬性。
當(dāng)路由器導(dǎo)航到這個(gè)路由時(shí),它會(huì)用 ?loadChildren
?字符串來(lái)動(dòng)態(tài)加載 ?AdminModule
?,然后把 ?AdminModule
?添加到當(dāng)前的路由配置中,最后,它把所請(qǐng)求的路由加載到目標(biāo) ?admin
?組件中。
惰性加載和重新配置工作只會(huì)發(fā)生一次,也就是在該路由首次被請(qǐng)求時(shí)。在后續(xù)的請(qǐng)求中,該模塊和路由都是立即可用的。
最后一步是把管理特性區(qū)從主應(yīng)用中完全分離開(kāi)。根模塊 ?AppModule
?既不能加載也不能引用 ?AdminModule
?及其文件。
在 ?app.module.ts
? 中,從頂部移除 ?AdminModule
?的導(dǎo)入語(yǔ)句,并且從 NgModule 的 ?imports
?數(shù)組中移除 ?AdminModule
?。
你已經(jīng)使用 ?CanActivate
?保護(hù) ?AdminModule
?了,它會(huì)阻止未授權(quán)用戶訪問(wèn)管理特性區(qū)。如果用戶未登錄,它就會(huì)跳轉(zhuǎn)到登錄頁(yè)。
但是路由器仍然會(huì)加載 ?AdminModule
?—— 即使用戶無(wú)法訪問(wèn)它的任何一個(gè)組件。理想的方式是,只有在用戶已登錄的情況下你才加載 ?AdminModule
?。
添加一個(gè) ?CanLoad
?守衛(wèi),它只在用戶已登錄并且嘗試訪問(wèn)管理特性區(qū)的時(shí)候,才加載 ?AdminModule
?一次。
現(xiàn)有的 ?AuthGuard
?的 ?checkLogin()
? 方法中已經(jīng)有了支持 ?CanLoad
?守衛(wèi)的基礎(chǔ)邏輯。
auth.guard.ts
?。@angular/router
? 導(dǎo)入 ?CanLoad
?接口。AuthGuard
?類(lèi)的 ?implements
?列表中。canLoad()
?:canLoad(route: Route): boolean {
const url = `/${route.path}`;
return this.checkLogin(url);
}
路由器會(huì)把 ?canLoad()
? 方法的 ?route
?參數(shù)設(shè)置為準(zhǔn)備訪問(wèn)的目標(biāo) URL。如果用戶已經(jīng)登錄了,?checkLogin()
? 方法就會(huì)重定向到那個(gè) URL。
現(xiàn)在,把 ?AuthGuard
?導(dǎo)入到 ?AppRoutingModule
?中,并把 ?AuthGuard
?添加到 ?admin
?路由的 ?canLoad
?數(shù)組中。完整的 ?admin
?路由是這樣的:
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canLoad: [AuthGuard]
},
除了按需加載模塊外,還可以通過(guò)預(yù)加載方式異步加載模塊。
當(dāng)應(yīng)用啟動(dòng)時(shí),?AppModule
?被急性加載,這意味著它會(huì)立即加載。而 ?AdminModule
? 只在用戶點(diǎn)擊鏈接時(shí)加載,這叫做惰性加載。
預(yù)加載允許你在后臺(tái)加載模塊,以便當(dāng)用戶激活某個(gè)特定的路由時(shí),就可以渲染這些數(shù)據(jù)了??紤]一下危機(jī)中心。它不是用戶看到的第一個(gè)視圖。默認(rèn)情況下,英雄列表才是第一個(gè)視圖。為了獲得最小的初始有效負(fù)載和最快的啟動(dòng)時(shí)間,你應(yīng)該急性加載 ?AppModule
?和 ?HeroesModule
?。
你可以惰性加載危機(jī)中心。但是,你幾乎可以肯定用戶會(huì)在啟動(dòng)應(yīng)用之后的幾分鐘內(nèi)訪問(wèn)危機(jī)中心。理想情況下,應(yīng)用啟動(dòng)時(shí)應(yīng)該只加載 ?AppModule
?和 ?HeroesModule
?,然后幾乎立即開(kāi)始后臺(tái)加載 ?CrisisCenterModule
?。在用戶瀏覽到危機(jī)中心之前,該模塊應(yīng)該已經(jīng)加載完畢,可供訪問(wèn)了。
在每次成功的導(dǎo)航后,路由器會(huì)在自己的配置中查找尚未加載并且可以預(yù)加載的模塊。是否加載某個(gè)模塊,以及要加載哪些模塊,取決于預(yù)加載策略。
?Router
?提供了兩種預(yù)加載策略:
策略 |
詳情 |
---|---|
不預(yù)加載 |
這是默認(rèn)值。惰性加載的特性區(qū)仍然會(huì)按需加載。 |
預(yù)加載 |
預(yù)加載所有惰性加載的特性區(qū)。 |
路由器或者完全不預(yù)加載或者預(yù)加載每個(gè)惰性加載模塊。 路由器還支持自定義預(yù)加載策略,以便完全控制要預(yù)加載哪些模塊以及何時(shí)加載。
本節(jié)將指導(dǎo)你把 ?CrisisCenterModule
?改成惰性加載的,并使用 ?PreloadAllModules
?策略來(lái)預(yù)加載所有惰性加載模塊。
修改路由配置,來(lái)惰性加載 ?CrisisCenterModule
?。修改的步驟和配置惰性加載 ?AdminModule
?時(shí)一樣。
CrisisCenterRoutingModule
?中的路徑從 ?crisis-center
? 改為空字符串。AppRoutingModule
?中添加一個(gè) ?crisis-center
? 路由。loadChildren
?字符串來(lái)加載 ?CrisisCenterModule
?。app.module.ts
? 中移除所有對(duì) ?CrisisCenterModule
?的引用。下面是打開(kāi)預(yù)加載之前的模塊修改版:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Router } from '@angular/router';
import { AppComponent } from './app.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { ComposeMessageComponent } from './compose-message/compose-message.component';
import { AppRoutingModule } from './app-routing.module';
import { HeroesModule } from './heroes/heroes.module';
import { AuthModule } from './auth/auth.module';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
HeroesModule,
AuthModule,
AppRoutingModule,
],
declarations: [
AppComponent,
ComposeMessageComponent,
PageNotFoundComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
import { NgModule } from '@angular/core';
import {
RouterModule, Routes,
} from '@angular/router';
import { ComposeMessageComponent } from './compose-message/compose-message.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { AuthGuard } from './auth/auth.guard';
const appRoutes: Routes = [
{
path: 'compose',
component: ComposeMessageComponent,
outlet: 'popup'
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canLoad: [AuthGuard]
},
{
path: 'crisis-center',
loadChildren: () => import('./crisis-center/crisis-center.module').then(m => m.CrisisCenterModule)
},
{ path: '', redirectTo: '/heroes', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component';
import { CrisisListComponent } from './crisis-list/crisis-list.component';
import { CrisisCenterComponent } from './crisis-center/crisis-center.component';
import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component';
import { CanDeactivateGuard } from '../can-deactivate.guard';
import { CrisisDetailResolverService } from './crisis-detail-resolver.service';
const crisisCenterRoutes: Routes = [
{
path: '',
component: CrisisCenterComponent,
children: [
{
path: '',
component: CrisisListComponent,
children: [
{
path: ':id',
component: CrisisDetailComponent,
canDeactivate: [CanDeactivateGuard],
resolve: {
crisis: CrisisDetailResolverService
}
},
{
path: '',
component: CrisisCenterHomeComponent
}
]
}
]
}
];
@NgModule({
imports: [
RouterModule.forChild(crisisCenterRoutes)
],
exports: [
RouterModule
]
})
export class CrisisCenterRoutingModule { }
你可以現(xiàn)在嘗試它,并確認(rèn)在點(diǎn)擊了“Crisis Center”按鈕之后加載了 ?CrisisCenterModule
?。
要為所有惰性加載模塊啟用預(yù)加載功能,請(qǐng)從 Angular 的路由模塊中導(dǎo)入 ?PreloadAllModules
?。
?RouterModule.forRoot()
? 方法的第二個(gè)參數(shù)接受一個(gè)附加配置選項(xiàng)對(duì)象。?preloadingStrategy
?就是其中之一。把 ?PreloadAllModules
?添加到 ?forRoot()
? 調(diào)用中:
RouterModule.forRoot(
appRoutes,
{
enableTracing: true, // <-- debugging purposes only
preloadingStrategy: PreloadAllModules
}
)
這項(xiàng)配置會(huì)讓 ?Router
?預(yù)加載器立即加載所有惰性加載路由(帶 ?loadChildren
?屬性的路由)。
當(dāng)訪問(wèn) ?http://localhost:4200
? 時(shí),?/heroes
? 路由立即隨之啟動(dòng),并且路由器在加載了 ?HeroesModule
?之后立即開(kāi)始加載 ?CrisisCenterModule
?。
目前,?AdminModule
?并沒(méi)有預(yù)加載,因?yàn)?nbsp;?CanLoad
?阻塞了它。
?PreloadAllModules
?策略不會(huì)加載被?CanLoad
?守衛(wèi)所保護(hù)的特性區(qū)。
幾步之前,你剛剛給 ?AdminModule
?中的路由添加了 ?CanLoad
?守衛(wèi),以阻塞加載那個(gè)模塊,直到用戶認(rèn)證結(jié)束。?CanLoad
?守衛(wèi)的優(yōu)先級(jí)高于預(yù)加載策略。
如果你要加載一個(gè)模塊并且保護(hù)它防止未授權(quán)訪問(wèn),請(qǐng)移除 ?canLoad
?守衛(wèi),只單獨(dú)依賴(lài)?CanActivate
?守衛(wèi)。
在很多場(chǎng)景下,預(yù)加載的每個(gè)惰性加載模塊都能正常工作。但是,考慮到低帶寬和用戶指標(biāo)等因素,可以為特定的特性模塊使用自定義預(yù)加載策略。
本節(jié)將指導(dǎo)你添加一個(gè)自定義策略,它只預(yù)加載 ?data.preload
? 標(biāo)志為 ?true
?路由?;叵胍幌?,你可以在路由的 ?data
?屬性中添加任何東西。
在 ?AppRoutingModule
?的 ?crisis-center
? 路由中設(shè)置 ?data.preload
? 標(biāo)志。
{
path: 'crisis-center',
loadChildren: () => import('./crisis-center/crisis-center.module').then(m => m.CrisisCenterModule),
data: { preload: true }
},
生成一個(gè)新的 ?SelectivePreloadingStrategy
?服務(wù)。
ng generate service selective-preloading-strategy
使用下列內(nèi)容替換 ?selective-preloading-strategy.service.ts
?:
import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class SelectivePreloadingStrategyService implements PreloadingStrategy {
preloadedModules: string[] = [];
preload(route: Route, load: () => Observable<any>): Observable<any> {
if (route.data?.['preload'] && route.path != null) {
// add the route path to the preloaded module array
this.preloadedModules.push(route.path);
// log the route path to the console
console.log('Preloaded: ' + route.path);
return load();
} else {
return of(null);
}
}
}
?SelectivePreloadingStrategyService
?實(shí)現(xiàn)了 ?PreloadingStrategy
?,它有一個(gè)方法 ?preload()
?。
路由器會(huì)用兩個(gè)參數(shù)來(lái)調(diào)用 ?preload()
? 方法:
?preload
?的實(shí)現(xiàn)要返回一個(gè) ?Observable
?。如果該路由應(yīng)該預(yù)加載,它就會(huì)返回調(diào)用加載器函數(shù)所返回的 ?Observable
?。如果該路由不應(yīng)該預(yù)加載,它就返回一個(gè) ?null
?值的 ?Observable
?對(duì)象。
在這個(gè)例子中,如果路由的 ?data.preload
? 標(biāo)志是真值,則 ?preload()
? 方法會(huì)加載該路由。
它的副作用是 ?SelectivePreloadingStrategyService
?會(huì)把所選路由的 ?path
?記錄在它的公共數(shù)組 ?preloadedModules
?中。
很快,你就會(huì)擴(kuò)展 ?AdminDashboardComponent
?來(lái)注入該服務(wù),并且顯示它的 ?preloadedModules
?數(shù)組。
但是首先,要對(duì) ?AppRoutingModule
?做少量修改。
SelectivePreloadingStrategyService
?導(dǎo)入到 ?AppRoutingModule
?中。
PreloadAllModules
?策略替換成對(duì) ?forRoot()
? 的調(diào)用,并且傳入這個(gè) ?SelectivePreloadingStrategyService
?。現(xiàn)在,編輯 ?AdminDashboardComponent
?以顯示這些預(yù)加載路由的日志。
SelectivePreloadingStrategyService
?(它是一個(gè)服務(wù))。
preloadedModules
?數(shù)組。現(xiàn)在文件如下:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { SelectivePreloadingStrategyService } from '../../selective-preloading-strategy.service';
@Component({
selector: 'app-admin-dashboard',
templateUrl: './admin-dashboard.component.html',
styleUrls: ['./admin-dashboard.component.css']
})
export class AdminDashboardComponent implements OnInit {
sessionId!: Observable<string>;
token!: Observable<string>;
modules: string[] = [];
constructor(
private route: ActivatedRoute,
preloadStrategy: SelectivePreloadingStrategyService
) {
this.modules = preloadStrategy.preloadedModules;
}
ngOnInit() {
// Capture the session ID if available
this.sessionId = this.route
.queryParamMap
.pipe(map(params => params.get('session_id') || 'None'));
// Capture the fragment if available
this.token = this.route
.fragment
.pipe(map(fragment => fragment || 'None'));
}
}
一旦應(yīng)用加載完了初始路由,?CrisisCenterModule
?也被預(yù)加載了。通過(guò) ?Admin
?特性區(qū)中的記錄就可以驗(yàn)證它,“Preloaded Modules”中列出了 ?crisis-center
?。它也被記錄到了瀏覽器的控制臺(tái)。
你已經(jīng)設(shè)置好了路由,并且用命令式和聲明式的方式導(dǎo)航到了很多不同的路由。但是,任何應(yīng)用的需求都會(huì)隨著時(shí)間而改變。你把鏈接 ?/heroes
? 和 ?hero/:id
? 指向了 ?HeroListComponent
?和 ?HeroDetailComponent
?組件。如果有這樣一個(gè)需求,要把鏈接 ?heroes
?變成 ?superheroes
?,你可能仍然希望以前的 URL 能正常導(dǎo)航。但你也不想在應(yīng)用中找到并修改每一個(gè)鏈接,這時(shí)候,重定向就可以省去這些瑣碎的重構(gòu)工作。
本節(jié)將指導(dǎo)你將 ?Hero
?路由遷移到新的 URL。在導(dǎo)航之前,?Router
?會(huì)檢查路由配置中的重定向語(yǔ)句,以便將來(lái)按需觸發(fā)重定向。要支持這種修改,你就要在 ?heroes-routing.module
? 文件中把老的路由重定向到新的路由。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroListComponent } from './hero-list/hero-list.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
const heroesRoutes: Routes = [
{ path: 'heroes', redirectTo: '/superheroes' },
{ path: 'hero/:id', redirectTo: '/superhero/:id' },
{ path: 'superheroes', component: HeroListComponent, data: { animation: 'heroes' } },
{ path: 'superhero/:id', component: HeroDetailComponent, data: { animation: 'hero' } }
];
@NgModule({
imports: [
RouterModule.forChild(heroesRoutes)
],
exports: [
RouterModule
]
})
export class HeroesRoutingModule { }
注意,這里有兩種類(lèi)型的重定向。第一種是不帶參數(shù)的從 ?/heroes
? 重定向到 ?/superheroes
?。這是一種非常直觀的重定向。第二種是從 ?/hero/:id
? 重定向到 ?/superhero/:id
?,它還要包含一個(gè) ?:id
? 路由參數(shù)。路由器重定向時(shí)使用強(qiáng)大的模式匹配功能,這樣,路由器就會(huì)檢查 URL,并且把 ?path
?中帶的路由參數(shù)替換成相應(yīng)的目標(biāo)形式。以前,你導(dǎo)航到形如 ?/hero/15
? 的 URL 時(shí),帶了一個(gè)路由參數(shù) ?id
?,它的值是 ?15
?。
在重定向的時(shí)候,路由器還支持查詢參數(shù)和片段(fragment)。
- 當(dāng)使用絕對(duì)地址重定向時(shí),路由器將會(huì)使用路由配置的 ?
redirectTo
?屬性中規(guī)定的查詢參數(shù)和片段。- 當(dāng)使用相對(duì)地址重定向時(shí),路由器將會(huì)使用源地址(跳轉(zhuǎn)前的地址)中的查詢參數(shù)和片段。
目前,空路徑被重定向到了 ?/heroes
?,它又被重定向到了 ?/superheroes
?。這樣不行,因?yàn)?nbsp;?Router
?在每一層的路由配置中只會(huì)處理一次重定向。這樣可以防止出現(xiàn)無(wú)限循環(huán)的重定向。
所以,你要在 ?app-routing.module.ts
? 中修改空路徑路由,讓它重定向到 ?/superheroes
?。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ComposeMessageComponent } from './compose-message/compose-message.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { AuthGuard } from './auth/auth.guard';
import { SelectivePreloadingStrategyService } from './selective-preloading-strategy.service';
const appRoutes: Routes = [
{
path: 'compose',
component: ComposeMessageComponent,
outlet: 'popup'
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canLoad: [AuthGuard]
},
{
path: 'crisis-center',
loadChildren: () => import('./crisis-center/crisis-center.module').then(m => m.CrisisCenterModule),
data: { preload: true }
},
{ path: '', redirectTo: '/superheroes', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
{
enableTracing: false, // <-- debugging purposes only
preloadingStrategy: SelectivePreloadingStrategyService,
}
)
],
exports: [
RouterModule
]
})
export class AppRoutingModule { }
由于 ?routerLink
?與路由配置無(wú)關(guān),所以你要修改相關(guān)的路由鏈接,以便在新的路由激活時(shí),它們也能保持激活狀態(tài)。還要修改 ?app.component.ts
? 模板中的 ?/heroes
? 這個(gè) ?routerLink
?。
<div class="wrapper">
<h1 class="title">Angular Router</h1>
<nav>
<a routerLink="/crisis-center" routerLinkActive="active" ariaCurrentWhenActive="page">Crisis Center</a>
<a routerLink="/superheroes" routerLinkActive="active" ariaCurrentWhenActive="page">Heroes</a>
<a routerLink="/admin" routerLinkActive="active" ariaCurrentWhenActive="page">Admin</a>
<a routerLink="/login" routerLinkActive="active" ariaCurrentWhenActive="page">Login</a>
<a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>
</nav>
<div [@routeAnimation]="getRouteAnimationData()">
<router-outlet></router-outlet>
</div>
<router-outlet name="popup"></router-outlet>
</div>
修改 ?hero-detail.component.ts
? 中的 ?goToHeroes()
? 方法,使用可選的路由參數(shù)導(dǎo)航回 ?/superheroes
?。
gotoHeroes(hero: Hero) {
const heroId = hero ? hero.id : null;
// Pass along the hero id if available
// so that the HeroList component can select that hero.
// Include a junk 'foo' property for fun.
this.router.navigate(['/superheroes', { id: heroId, foo: 'foo' }]);
}
當(dāng)這些重定向設(shè)置好之后,所有以前的路由都指向了它們的新目標(biāo),并且每個(gè) URL 也仍然能正常工作。
要確定你的路由是否真的按照正確的順序執(zhí)行的,你可以審查路由器的配置。
可以通過(guò)注入路由器并在控制臺(tái)中記錄其 ?config
?屬性來(lái)實(shí)現(xiàn)。比如,把 ?AppModule
?修改為這樣,并在瀏覽器的控制臺(tái)窗口中查看最終的路由配置。
export class AppModule {
// Diagnostic only: inspect router configuration
constructor(router: Router) {
// Use a custom replacer to display function names in the route configs
const replacer = (key, value) => (typeof value === 'function') ? value.name : value;
console.log('Routes: ', JSON.stringify(router.config, replacer, 2));
}
}
對(duì)這個(gè)已完成的路由器應(yīng)用,參見(jiàn) 現(xiàn)場(chǎng)演練 / 下載范例的最終代碼。
更多建議: