使用懒加载模块
了解在使用懒加载模块时常见的错误以及如何避免它们。
懒加载模块简介
当 Angular 应用程序启动时,所有模块都会随之加载。这种行为称为 急切加载(eager loading),也是 Angular 模块的默认行为。然而,如果我们指定某个模块为 懒加载(lazy-loaded),它将仅在用户请求时才被加载。
在编译过程中,每个懒加载模块都会被独立打包。这有助于减小初始包的体积,从而降低加载时间。
在我们的应用中,已将产品模块定义为懒加载模块。我们可以通过 Chrome 开发者工具来验证产品模块是否真正实现了懒加载。点击下方的“运行”按钮以启动应用并访问首页。
No configuration found for ID: efad79f8cfd8c56356c6d8c3777701326a46e612ef132955b1542970a6b120a9
要打开浏览器开发者工具,请右键点击浏览器窗口,选择“检查”选项。然后切换到“网络(Network)”标签页。

点击首页的“查看详情”按钮。在“网络”标签页中会出现一个单独的 JavaScript 文件。这个文件是在编译时独立打包的,并未在初始加载时被引入。每个懒加载模块仅在首次请求时加载一次。

常见错误
创建懒加载模块的主要目标是彻底将该模块的功能与初始打包代码解耦。但开发者常常会从懒加载模块中导入接口到急切加载模块中,导致懒加载模块的功能意外地被包含在初始打包代码中。
我们在 app.interfaces.ts 文件(位于产品模块之外)中导入了 ProductState(该接口定义在产品模块内部),这破坏了产品模块的懒加载边界。我们故意这样写,以便学习者能够理解这一常见错误,并在未来避免犯同样的问题。
src/app/app.interfaces.ts
1import { ProductState } from './product/state/product.interfaces'; 2import { ProductsState } from './products/state/products.interfaces'; 3 4export interface Product { 5 id: number; 6 title: string; 7 author: string; 8 description: string; 9 imgUrl: string; 10 price: number; 11 quantity: number; 12 cart: number; 13} 14 15export interface AppState { 16 products: ProductsState; 17 product: ProductState; 18}
src/app/product/state/product.interfaces.ts
1import { Product } from "src/app/app.interfaces"; 2 3export type ProductState = Product;
现在我们来修复这段代码。首先,从全局 AppState 中移除 product: ProductState。
src/app/app.interfaces.ts
1import { ProductsState } from './products/state/products.interfaces'; 2 3export interface Product { 4 id: number; 5 title: string; 6 author: string; 7 description: string; 8 imgUrl: string; 9 price: number; 10 quantity: number; 11 cart: number; 12} 13 14export interface AppState { 15 products: ProductsState; 16 // 已移除 "product: ProductState" 17}
接着,我们将全局 AppState 扩展,把 product: ProductState 属性添加到 product.interfaces.ts 文件中。
src/app/product/state/product.interfaces.ts
1import * as States from 'src/app/app.interfaces'; 2 3export type ProductState = States.Product; 4 5export interface AppState extends States.AppState { 6 product: ProductState; 7}
最后,将 app.interfaces.ts 文件中的全局 AppState 替换为来自 src/app/product/state/product.interfaces.ts 的扩展版 AppState。该扩展后的 AppState 可在 product.component.ts 文件中找到。
src/app/product/product.component.ts
1import { Component, OnInit } from '@angular/core'; 2import { ActivatedRoute } from '@angular/router'; 3import { Store } from '@ngrx/store'; 4import { Product } from '../app.interfaces'; 5import { ProductsService } from '../products.service'; 6import { getProductAction } from './state/product.actions'; 7// import { AppState } from '../app.interfaces'; 8import { AppState } from './state/product.interfaces'; 9import { getProductSelector } from './state/product.selectors'; 10 11@Component({ 12 selector: 'app-product', 13 templateUrl: './product.component.html', 14 styleUrls: ['./product.component.scss'], 15}) 16export class ProductComponent implements OnInit { 17 id: number = 0; 18 product$ = this.store.select<Product>(getProductSelector); 19 20 constructor( 21 private store: Store<AppState>, 22 private productsService: ProductsService, 23 private route: ActivatedRoute 24 ) {} 25 26 ngOnInit(): void { 27 this.route.params.subscribe((params) => (this.id = params['id'])); 28 29 this.getProduct(this.id); 30 } 31 32 getProduct(id: number) { 33 this.productsService 34 .getProduct(id) 35 .subscribe((product: Product) => (this.store.dispatch(getProductAction({ product })))); 36 } 37}
现在,ProductModule 的代码已完全与初始打包代码解耦。更新后的项目代码如下:
No configuration found for ID: a8829563f5f87fade034dd78576e2b79f59d44ed317ddb11c74eaf605b1b18ec
最后,本节课的核心要点是:在使用懒加载模块时务必谨慎。确保懒加载模块的代码与其他模块完全隔离,不产生不必要的依赖。