如何避免Angular/Webpack中懒加载模块重复打包共享模块?
我的场景
最近做Angular项目时碰到个头疼的问题:项目里有这些模块:
- AppModule:CLI生成的主模块
- SharedModule:自己封装的通用共享模块
- MaterialModule:统一管理Angular Material组件的模块
- CustomModuleA、CustomModuleB:两个懒加载模块
目前SharedModule和MaterialModule已经跟着AppModule在应用启动时就加载了,但这俩模块在两个懒加载模块里也得用。结果发现,虽然这俩依赖已经提前加载好了,但懒加载模块的打包chunk里居然还包含了它们的代码,导致冗余打包,拖慢加载速度。
我就琢磨,有没有办法让Angular编译器知道这些依赖已经在全局提供了,不用再打包进懒加载模块里?就像Maven里的provided作用域那样,只在编译时依赖,运行时由全局环境提供。
摸索出来的解决方案
查了文档加实际测试后,总结出几个可行的方法,分情况来看:
1. 服务类依赖:用providedIn: 'root'替代模块providers
如果你的SharedModule里有服务类,别再把它们放在模块的providers数组里了。直接在服务类上加上@Injectable({ providedIn: 'root' }):
@Injectable({ providedIn: 'root' // 注册到根注入器,全局唯一实例 }) export class SharedService { // ... }
这样Angular会自动把服务注册在根注入器,不管哪个模块调用它,都只会在主bundle里打包一次,懒加载模块里用的时候也不会重复打包。
2. 共享模块(如Shared/Material):用forRoot()模式分离全局配置
对于SharedModule、MaterialModule这类包含组件/指令/管道的模块,我们可以用Angular推荐的forRoot()模式,把全局服务和共享组件分开处理:
先改造SharedModule:
@NgModule({ declarations: [/* 你的共享组件、指令、管道 */], exports: [/* 导出给其他模块用的内容 */] }) export class SharedModule { // 静态forRoot方法,仅在根模块调用一次 static forRoot(): ModuleWithProviders<SharedModule> { return { ngModule: SharedModule, providers: [/* 这里放只需要全局初始化一次的服务 */] }; } }
然后在AppModule里导入时用SharedModule.forRoot(),把全局服务注册到根注入器;而在懒加载模块(CustomModuleA/B)里,只需要导入SharedModule本身(不用加forRoot()),这样懒加载模块只会用到SharedModule里的组件,不会重复注册服务,构建优化器也能识别到这些组件已经在主bundle里了。
如果你自己封装了MaterialModule,也可以用同样的模式处理。
3. 开启生产构建优化是关键
一定要用生产模式构建项目:
ng build --prod
Angular的生产构建默认会开启build-optimizer和tree-shaking,这两个优化器会自动检测已经在主bundle里存在的模块/代码,不会把它们重复打包进懒加载的chunk里。
如果需要手动确认配置,可以打开angular.json,检查项目的build配置里optimization是否为true:
"projects": { "your-project": { "architect": { "build": { "options": { "optimization": true, // 其他配置... } } } } }
4. 避坑:不要在懒加载模块里重复注册全局服务
很多人会不小心在懒加载模块的providers里再次声明全局服务,或者重复导入带有providers的模块,这会导致Angular重新打包这些服务。所以一定要确保全局服务要么用providedIn: 'root',要么放在forRoot()里,只在根模块注册一次。
最后总结
按照这几步操作,就能实现类似Maven provided作用域的效果:
- 改造共享模块为
forRoot()模式,分离全局服务和共享组件; - 根模块用
forRoot()导入,懒加载模块仅导入模块本身; - 服务类用
providedIn: 'root'全局注册; - 生产模式构建开启优化。
这样懒加载模块的chunk里就不会再包含已经在主bundle里的依赖了,减少冗余,提升加载速度。
内容的提问来源于stack exchange,提问作者Antoniossss

