填寫這份《一分鐘調查》,幫我們(開發組)做得更好!去填寫Home

延遲載入特性模組

Lazy-loading feature modules

預設情況下,NgModule 都是急性載入的,也就是說它會在應用載入時儘快載入,所有模組都是如此,無論是否立即要用。對於帶有很多路由的大型應用,考慮使用延遲載入 —— 一種按需載入 NgModule 的模式。延遲載入可以減小初始套件的尺寸,從而減少載入時間。

By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading—a design pattern that loads NgModules as needed. Lazy loading helps keep initial bundle sizes smaller, which in turn helps decrease load times.

如果需要本頁描述的具有兩個延遲載入模組的範例應用,參閱現場演練 / 下載範例

For the final sample app with two lazy-loaded modules that this page describes, see the現場演練 / 下載範例.

延遲載入入門

Lazy loading basics

本節會介紹配置延遲載入路由的基本過程。 想要一個分步的範例,參閱本頁的分步設定部分。

This section introduces the basic procedure for configuring a lazy-loaded route. For a step-by-step example, see the step-by-step setup section on this page.

要延遲載入 Angular 模組,請在 AppRoutingModule routes 中使用 loadchildren 代替 component 進行配置,程式碼如下。

To lazy load Angular modules, use loadchildren (instead of component) in your AppRoutingModule routes configuration as follows.

const routes: Routes = [ { path: 'items', loadChildren: () => import('./items/items.module').then(m => m.ItemsModule) } ];
AppRoutingModule (excerpt)
      
      const routes: Routes = [
  {
    path: 'items',
    loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
  }
];
    

在延遲載入模組的路由模組中,新增一個指向該元件的路由。

In the lazy-loaded module's routing module, add a route for the component.

const routes: Routes = [ { path: '', component: ItemsComponent } ];
Routing module for lazy loaded module (excerpt)
      
      const routes: Routes = [
  {
    path: '',
    component: ItemsComponent
  }
];
    

還要確保從 AppModule 中移除了 ItemsModule。 想要一個關於延遲載入模組的分步操作指南,請繼續檢視本頁的後續章節。

Also be sure to remove the ItemsModule from the AppModule. For step-by-step instructions on lazy loading modules, continue with the following sections of this page.

分步設定

Step-by-step setup

建立延遲載入的特性模組有兩個主要步驟:

There are two main steps to setting up a lazy-loaded feature module:

  1. 使用 --route 標誌,用 CLI 建立特性模組。

    Create the feature module with the CLI, using the --route flag.

  2. 配置相關路由。

    Configure the routes.

建立應用

Set up an app

如果你還沒有應用,可以遵循下面的步驟使用 CLI 建立一個。如果已經有了,可以直接跳到 配置路由部分。 輸入下列命令,其中的 customer-app 表示你的應用名稱:

If you don’t already have an app, you can follow the steps below to create one with the CLI. If you already have an app, skip to Configure the routes. Enter the following command where customer-app is the name of your app:

ng new customer-app --routing
      
      ng new customer-app --routing
    

這會建立一個名叫 customer-app 的應用,而 --routing 標識生成了一個名叫 app-routing.module.ts 的檔案,它是你建立延遲載入的特性模組時所必須的。 輸入命令 cd customer-app 進入該專案。

This creates an app called customer-app and the --routing flag generates a file called app-routing.module.ts, which is one of the files you need for setting up lazy loading for your feature module. Navigate into the project by issuing the command cd customer-app.

--routing 選項需要 Angular/CLI 8.1 或更高版本。 請參閱保持最新

The --routing option requires Angular/CLI version 8.1 or higher. See Keeping Up to Date.

建立一個帶路由的特性模組

Create a feature module with routing

接下來,你將需要一個包含路由的目標元件的特性模組。 要建立它,在終端中輸入如下命令,其中 customers 是特性模組的名稱。載入 customers 特性模組的路徑也是 customers,因為它是透過 --route 選項指定的:

Next, you’ll need a feature module with a component to route to. To make one, enter the following command in the terminal, where customers is the name of the feature module. The path for loading the customers feature modules is also customers because it is specified with the --route option:

ng generate module customers --route customers --module app.module
      
      ng generate module customers --route customers --module app.module
    

這將建立一個 customers 資料夾,在其 customers.module.ts 檔案中定義了新的可延遲載入模組 CustomersModule。該命令會自動在新特性模組中宣告 CustomersComponent

This creates a customers folder having the new lazy-loadable feature module CustomersModule defined in the customers.module.ts file and the routing module CustomersRoutingModule defined in the customers-routing.module.ts file. The command automatically declares the CustomersComponent and imports CustomersRoutingModule inside the new feature module.

因為這個新模組想要延遲載入,所以該命令不會在應用的根模組 app.module.ts 中新增對新特性模組的參考。 相反,它將宣告的路由 customers 新增到以 --module 選項指定的模組中宣告的 routes 陣列中。

Because the new module is meant to be lazy-loaded, the command does NOT add a reference to the new feature module in the application's root module file, app.module.ts. Instead, it adds the declared route, customers to the routes array declared in the module provided as the --module option.

const routes: Routes = [ { path: 'customers', loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) } ];
src/app/app-routing.module.ts
      
      const routes: Routes = [
  {
    path: 'customers',
    loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule)
  }
];
    

注意,延遲載入語法使用 loadChildren,其後是一個使用瀏覽器內建的 import('...') 語法進行動態匯入的函式。 其匯入路徑是到當前模組的相對路徑。

Notice that the lazy-loading syntax uses loadChildren followed by a function that uses the browser's built-in import('...') syntax for dynamic imports. The import path is the relative path to the module.

新增另一個特性模組

Add another feature module

使用同樣的命令建立第二個帶路由的延遲載入特性模組及其樁元件。

Use the same command to create a second lazy-loaded feature module with routing, along with its stub component.

ng generate module orders --route orders --module app.module
      
      ng generate module orders --route orders --module app.module
    

這將建立一個名為 orders 的新資料夾,其中包含 OrdersModuleOrdersRoutingModule 以及新的 OrdersComponent 原始檔。 使用 --route 選項指定的 orders 路由,用延遲載入語法新增到了 app-routing.module.ts 檔案內的 routes 陣列中。

This creates a new folder called orders containing the OrdersModule and OrdersRoutingModule, along with the new OrdersComponent source files. The orders route, specified with the --route option, is added to the routes array inside the app-routing.module.ts file, using the lazy-loading syntax.

const routes: Routes = [ { path: 'customers', loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) }, { path: 'orders', loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule) } ];
src/app/app-routing.module.ts
      
      const routes: Routes = [
  {
    path: 'customers',
    loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule)
  },
  {
    path: 'orders',
    loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule)
  }
];
    

建立 UI

Set up the UI

雖然你也可以在位址列中輸入 URL,不過導航 UI 會更好用,也更常見。 把 app.component.html 中的佔位指令碼替換成一個自訂的導航,以便你在瀏覽器中能輕鬆地在模組之間導航。

Though you can type the URL into the address bar, a navigation UI is easier for the user and more common. Replace the default placeholder markup in app.component.html with a custom nav so you can easily navigate to your modules in the browser:

<h1> {{title}} </h1> <button routerLink="/customers">Customers</button> <button routerLink="/orders">Orders</button> <button routerLink="">Home</button> <router-outlet></router-outlet>
app.component.html
      
      <h1>
  {{title}}
</h1>

<button routerLink="/customers">Customers</button>
<button routerLink="/orders">Orders</button>
<button routerLink="">Home</button>

<router-outlet></router-outlet>
    

要想在瀏覽器中看到你的應用,就在終端視窗中輸入下列命令:

To see your app in the browser so far, enter the following command in the terminal window:

ng serve
      
      ng serve
    

然後,跳轉到 localhost:4200,這時你應該看到 customer-app 和三個按鈕。

Then go to localhost:4200 where you should see customer-app and three buttons.

這些按鈕生效了,因為 CLI 會自動將特性模組的路由新增到 app.module.ts 中的 routes 陣列中。

These buttons work, because the CLI automatically added the routes to the feature modules to the routes array in app.module.ts.

匯入與路由配置

Imports and route configuration

CLI 會將每個特性模組自動新增到應用級的路由對映表中。 透過新增預設路由來最終完成這些步驟。 在 app-routing.module.ts 檔案中,使用如下命令更新 routes 陣列:

The CLI automatically added each feature module to the routes map at the application level. Finish this off by adding the default route. In the app-routing.module.ts file, update the routes array with the following:

const routes: Routes = [ { path: 'customers', loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) }, { path: 'orders', loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule) }, { path: '', redirectTo: '', pathMatch: 'full' } ];
src/app/app-routing.module.ts
      
      const routes: Routes = [
  {
    path: 'customers',
    loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule)
  },
  {
    path: 'orders',
    loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule)
  },
  {
    path: '',
    redirectTo: '',
    pathMatch: 'full'
  }
];
    

前兩個路徑是到 CustomersModuleOrdersModule 的路由。 最後一個條目則定義了預設路由。空路徑匹配所有不匹配先前路徑的內容。

The first two paths are the routes to the CustomersModule and the OrdersModule. The final entry defines a default route. The empty path matches everything that doesn't match an earlier path.

特性模組內部

Inside the feature module

接下來,仔細看看 customers.module.ts 檔案。如果你使用的是 CLI,並按照此頁面中的步驟進行操作,則無需在此處執行任何操作。

Next, take a look at the customers.module.ts file. If you’re using the CLI and following the steps outlined in this page, you don’t have to do anything here.

import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CustomersRoutingModule } from './customers-routing.module'; import { CustomersComponent } from './customers.component'; @NgModule({ imports: [ CommonModule, CustomersRoutingModule ], declarations: [CustomersComponent] }) export class CustomersModule { }
src/app/customers/customers.module.ts
      
      import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomersRoutingModule } from './customers-routing.module';
import { CustomersComponent } from './customers.component';

@NgModule({
  imports: [
    CommonModule,
    CustomersRoutingModule
  ],
  declarations: [CustomersComponent]
})
export class CustomersModule { }
    

customers.module.ts 檔案匯入了 customers-routing.module.tscustomers.component.ts 檔案。@NgModuleimports 陣列中列出了 CustomersRoutingModule,讓 CustomersModule 可以訪問它自己的路由模組。CustomersComponent 位於 declarations 陣列中,這意味著 CustomersComponent 屬於 CustomersModule

The customers.module.ts file imports the customers-routing.module.ts and customers.component.ts files. CustomersRoutingModule is listed in the @NgModule imports array giving CustomersModule access to its own routing module. CustomersComponent is in the declarations array, which means CustomersComponent belongs to the CustomersModule.

然後,app-routing.module.ts 會使用 JavaScript 的動態匯入功能來匯入特性模組 customers.module.ts

The app-routing.module.ts then imports the feature module, customers.module.ts using JavaScript's dynamic import.

專屬於特性模組的路由定義檔案 customers-routing.module.ts 將匯入在 customers.component.ts 檔案中定義的自有特性元件,以及其它 JavaScript 匯入語句。然後將空路徑對映到 CustomersComponent

The feature-specific route definition file customers-routing.module.ts imports its own feature component defined in the customers.component.ts file, along with the other JavaScript import statements. It then maps the empty path to the CustomersComponent.

import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { CustomersComponent } from './customers.component'; const routes: Routes = [ { path: '', component: CustomersComponent } ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) export class CustomersRoutingModule { }
src/app/customers/customers-routing.module.ts
      
      import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { CustomersComponent } from './customers.component';


const routes: Routes = [
  {
    path: '',
    component: CustomersComponent
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class CustomersRoutingModule { }
    

這裡的 path 設定為空字串,因為 AppRoutingModule 中的路徑已經設定為 customers,因此,CustomersRoutingModule 中的此路由已經位於 customers 這個上下文中。此路由模組中的每個路由都是其子路由。

The path here is set to an empty string because the path in AppRoutingModule is already set to customers, so this route in the CustomersRoutingModule, is already within the customers context. Every route in this routing module is a child route.

另一個特性模組中路由模組的配置也類似。

The other feature module's routing module is configured similarly.

import { OrdersComponent } from './orders.component'; const routes: Routes = [ { path: '', component: OrdersComponent } ];
src/app/orders/orders-routing.module.ts (excerpt)
      
      import { OrdersComponent } from './orders.component';

const routes: Routes = [
  {
    path: '',
    component: OrdersComponent
  }
];
    

確認它工作正常

Verify lazy loading

你可以使用 Chrome 開發者工具來確認一下這些模組真的是延遲載入的。 在 Chrome 中,按 Cmd+Option+i(Mac)或 Ctrl+Shift+j(PC),並選中 Network 頁標籤。

You can check to see that a module is indeed being lazy loaded with the Chrome developer tools. In Chrome, open the dev tools by pressing Cmd+Option+i on a Mac or Ctrl+Shift+j on a PC and go to the Network Tab.

點選 Orders 或 Customers 按鈕。如果你看到某個 chunk 檔案出現了,就表示一切就緒,特性模組被延遲載入成功了。Orders 和 Customers 都應該出現一次 chunk,並且它們各自只應該出現一次。

Click on the Orders or Customers button. If you see a chunk appear, everything is wired up properly and the feature module is being lazy loaded. A chunk should appear for Orders and for Customers but will only appear once for each.

要想再次檢視它或測試本專案後面的行為,只要點選 Network 頁左上放的 清除 圖示即可。

To see it again, or to test after working in the project, clear everything out by clicking the circle with a line through it in the upper left of the Network Tab:

然後,使用 Cmd+r(Mac) 或 Ctrl+r(PC) 重新載入頁面。

Then reload with Cmd+r or Ctrl+r, depending on your platform.

forRoot()forChild()

forRoot() and forChild()

你可能已經注意到了,CLI 會把 RouterModule.forRoot(routes) 新增到 AppRoutingModuleimports 陣列中。 這會讓 Angular 知道 AppRoutingModule 是一個路由模組,而 forRoot() 表示這是一個根路由模組。 它會配置你傳入的所有路由、讓你能訪問路由器指令並註冊 RouterforRoot() 在應用中只應該使用一次,也就是這個 AppRoutingModule 中。

You might have noticed that the CLI adds RouterModule.forRoot(routes) to the AppRoutingModule imports array. This lets Angular know that the AppRoutingModule is a routing module and forRoot() specifies that this is the root routing module. It configures all the routes you pass to it, gives you access to the router directives, and registers the Router service. Use forRoot() only once in the application, inside the AppRoutingModule.

CLI 還會把 RouterModule.forChild(routes) 新增到各個特性模組中。這種方式下 Angular 就會知道這個路由列表只負責提供額外的路由並且其設計意圖是作為特性模組使用。你可以在多個模組中使用 forChild()

The CLI also adds RouterModule.forChild(routes) to feature routing modules. This way, Angular knows that the route list is only responsible for providing additional routes and is intended for feature modules. You can use forChild() in multiple modules.

forRoot() 方法為路由器管理全域性性的注入器配置。 forChild() 方法中沒有注入器配置,只有像 RouterOutletRouterLink 這樣的指令。 欲知詳情,參閱單例服務章的 forRoot() 模式小節。

The forRoot() method takes care of the global injector configuration for the Router. The forChild() method has no injector configuration. It uses directives such as RouterOutlet and RouterLink. For more information, see the forRoot() pattern section of the Singleton Services guide.

預載入

Preloading

預載入透過在後臺載入部分應用來改進使用者體驗。你可以預載入模組或元件資料。

Preloading improves UX by loading parts of your app in the background. You can preload modules or component data.

預載入模組

Preloading modules

預載入模組透過在後臺載入部分應用來改善使用者體驗,這樣使用者在啟用路由時就無需等待下載這些元素。

Preloading modules improves UX by loading parts of your app in the background so users don't have to wait for the elements to download when they activate a route.

要啟用所有延遲載入模組的預載入, 請從 Angular 的 router 匯入 PreloadAllModules 令牌。

To enable preloading of all lazy loaded modules, import the PreloadAllModules token from the Angular router.

import { PreloadAllModules } from '@angular/router';
AppRoutingModule (excerpt)
      
      import { PreloadAllModules } from '@angular/router';
    

還是在 AppRoutingModule 中,透過 forRoot() 指定你的預載入策略。

Still in the AppRoutingModule, specify your preloading strategy in forRoot().

RouterModule.forRoot( appRoutes, { preloadingStrategy: PreloadAllModules } )
AppRoutingModule (excerpt)
      
      RouterModule.forRoot(
  appRoutes,
  {
    preloadingStrategy: PreloadAllModules
  }
)
    

預載入元件資料

Preloading component data

要預載入元件資料,你可以使用 resolver 守衛。解析器透過阻止頁面載入來改進使用者體驗,直到顯示頁面時的全部必要資料都可用。

To preload component data, you can use a resolver. Resolvers improve UX by blocking the page load until all necessary data is available to fully display the page.

解析器

Resolvers

建立一個解析器服務。透過 CLI,產生服務的命令如下:

Create a resolver service. With the CLI, the command to generate a service is as follows:

ng generate service
      
      ng generate service 

    

在你的服務中,匯入下列路由器成員,實現 Resolve 介面,並注入到 Router 服務中:

In your service, import the following router members, implement Resolve, and inject the Router service:

import { Resolve } from '@angular/router'; ... export class CrisisDetailResolverService implements Resolve<> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<> { // your logic goes here } }
Resolver service (excerpt)
      
      import { Resolve } from '@angular/router';

...

export class CrisisDetailResolverService implements Resolve<> {
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<> {
    // your logic goes here
  }
}
    

把這個解析器匯入此模組的路由模組。

Import this resolver into your module's routing module.

import { YourResolverService } from './your-resolver.service';
Feature module's routing module (excerpt)
      
      import { YourResolverService }    from './your-resolver.service';
    

在元件的 route 配置中新增一個 resolve 物件。

Add a resolve object to the component's route configuration.

{ path: '/your-path', component: YourComponent, resolve: { crisis: YourResolverService } }
Feature module's routing module (excerpt)
      
      {
  path: '/your-path',
  component: YourComponent,
  resolve: {
    crisis: YourResolverService
  }
}
    

在此元件中,使用一個 Observable 來從 ActivatedRoute 獲取資料。

In the component, use an Observable to get the data from the ActivatedRoute.

ngOnInit() { this.route.data .subscribe((your-parameters) => { // your data-specific code goes here }); }
Component (excerpt)
      
      ngOnInit() {
  this.route.data
    .subscribe((your-parameters) => {
      // your data-specific code goes here
    });
}
    

關於工作範例的更多資訊,請參閱路由課程的預載入部分

For more information with a working example, see the routing tutorial section on preloading.


更多關於 NgModule 和路由的知識

More on NgModules and routing

你可能還對下列內容感興趣:

You may also be interested in the following: