非Route模块的Lazy Load:如何懒加载被其他Component/Module引用的模块
非路由模块(LibModule)的懒加载实现方案
针对你提到的场景——LibModule包含仅被其他模块组件调用的自定义组件,无需通过路由直接访问,我们可以通过动态导入+Angular模块/组件动态创建的方式实现懒加载,具体分为以下几个步骤:
1. 配置LibModule,避免提前加载
首先确保LibModule不会被任何模块的imports数组直接引用(否则会被打包进主chunk,失去懒加载意义),同时导出需要使用的组件:
// lib.module.ts import { NgModule } from '@angular/core'; import { GridComponent } from './grid/grid.component'; import { ButtonComponent } from './button/button.component'; @NgModule({ declarations: [GridComponent, ButtonComponent], exports: [GridComponent, ButtonComponent] // 导出组件,方便加载后实例化 }) export class LibModule { }
2. 方式一:在组件中动态加载并使用LibModule的组件
如果只是在某个组件(比如HomeComponent)中按需使用LibModule的组件,可以通过动态导入模块+创建组件实例的方式实现:
步骤2.1:在组件模板中预留挂载容器
<!-- home.component.html --> <!-- 预留动态组件的挂载容器 --> <div #libComponentHost></div> <button (click)="loadGridComponent()">加载Grid组件</button>
步骤2.2:在组件类中实现动态加载逻辑
利用Angular的ViewContainerRef、Injector和动态导入语法import(),按需加载LibModule并创建组件:
// home.component.ts import { Component, ViewChild, ViewContainerRef, Injector } from '@angular/core'; import { createNgModule } from '@angular/core'; @Component({ selector: 'app-home', templateUrl: './home.component.html' }) export class HomeComponent { // 获取挂载容器的引用 @ViewChild('libComponentHost', { read: ViewContainerRef, static: false }) componentHost!: ViewContainerRef; constructor(private injector: Injector) { } async loadGridComponent() { // 清空容器(可选,避免重复创建) this.componentHost.clear(); // 1. 动态加载LibModule const { LibModule } = await import('../lib/lib.module'); // 2. 创建LibModule的引用 const moduleRef = await createNgModule(LibModule, this.injector); // 3. 动态加载需要的组件 const { GridComponent } = await import('../lib/grid/grid.component'); // 4. 创建组件实例并挂载到容器中 const componentRef = this.componentHost.createComponent(GridComponent, { injector: moduleRef.injector }); // 如果需要给组件传值,可以通过componentRef.instance设置 // componentRef.instance.someInput = 'Hello'; } }
3. 方式二:封装懒加载服务,复用模块加载逻辑
如果多个组件/模块都需要使用LibModule的组件,建议封装一个服务来管理模块的加载,避免重复加载:
步骤3.1:创建Lib加载服务
// lib-loader.service.ts import { Injectable, Injector, NgModuleRef } from '@angular/core'; import { LibModule } from '../lib/lib.module'; @Injectable({ providedIn: 'root' }) export class LibLoaderService { private libModuleRef: NgModuleRef<LibModule> | null = null; constructor(private injector: Injector) { } // 确保模块只被加载一次 async getLibModuleRef(): Promise<NgModuleRef<LibModule>> { if (this.libModuleRef) { return this.libModuleRef; } const { LibModule } = await import('../lib/lib.module'); this.libModuleRef = await createNgModule(LibModule, this.injector); return this.libModuleRef; } }
步骤3.2:在组件中使用服务加载组件
// home.component.ts import { Component, ViewChild, ViewContainerRef } from '@angular/core'; import { LibLoaderService } from './lib-loader.service'; @Component({ selector: 'app-home', templateUrl: './home.component.html' }) export class HomeComponent { @ViewChild('libComponentHost', { read: ViewContainerRef, static: false }) componentHost!: ViewContainerRef; constructor(private libLoader: LibLoaderService) { } async loadButtonComponent() { this.componentHost.clear(); // 获取已加载的模块引用 const moduleRef = await this.libLoader.getLibModuleRef(); // 动态加载Button组件 const { ButtonComponent } = await import('../lib/button/button.component'); // 创建组件实例 this.componentHost.createComponent(ButtonComponent, { injector: moduleRef.injector }); } }
4. 确保代码分割生效
Angular CLI默认会对import()语法的模块进行代码分割,将LibModule打包为独立的chunk。你可以通过ng build --prod后查看dist目录下的文件,会发现类似lib-lib-module.js的独立chunk文件,说明懒加载配置成功。
注意事项
- 不要在任何模块的
imports数组中直接引入LibModule,否则会被提前打包。 - 如果LibModule中包含服务,若服务是
providedIn: 'root'则全局可用;若服务是providedIn: LibModule,则服务的实例会绑定到LibModule的injector中,只有通过模块引用的injector才能获取。 - Angular 9+使用Ivy编译器后,无需手动配置
entryComponents,组件会被自动识别。
内容的提问来源于stack exchange,提问作者Vijay Chauhan
相关产品推荐
相关产品推荐

