Angular 5懒加载模块依赖报错:AOT编译时出现未定义提供者问题
兄弟,我太懂你这种JIT模式下跑的好好的,一执行ng build --prod开启AOT就报错的痛苦了!Angular 5的AOT预编译对模块依赖的规则比JIT严格太多,尤其是懒加载模块之间的直接依赖,完全踩中了AOT的编译禁忌。
问题根源
JIT编译是在浏览器运行时动态解析模块依赖,对懒加载模块之间的交叉依赖容忍度很高;但AOT是在构建阶段就把所有组件、模块预编译成JS,要求所有依赖必须在编译时就能被明确解析。懒加载模块本来是要被拆分到独立的chunk里按需加载的,互相直接依赖会让AOT编译器搞不清编译顺序,找不到对应模块的元数据,自然就报错了。
具体解决方案
1. 重构依赖:把共享逻辑抽到Shared模块
这是最规范也最彻底的解决办法。懒加载模块之间绝对不应该直接依赖,你需要把两个模块共用的服务、组件、路由解析器等抽到SharedModule里,让两个懒加载模块都依赖SharedModule而非彼此。
举个例子,假设message模块依赖user模块的路由解析器,现在把解析器移到Shared模块:
// shared.module.ts import { NgModule, ModuleWithProviders } from '@angular/core'; import { CommonModule } from '@angular/common'; import { UserRouteResolver } from './resolvers/user-route.resolver'; @NgModule({ imports: [CommonModule], declarations: [], exports: [] }) export class SharedModule { // 用forRoot()确保服务只在根模块提供一次,避免重复实例化 static forRoot(): ModuleWithProviders { return { ngModule: SharedModule, providers: [UserRouteResolver] }; } }
然后在根模块AppModule里导入SharedModule.forRoot(),在message和user模块里只导入SharedModule(不用forRoot),这样两个懒加载模块就能通过Shared模块拿到需要的解析器,又不会产生直接依赖。
2. 绕过模块依赖:用路由参数传递数据
如果你的需求只是从另一个懒加载模块获取路由数据,完全没必要依赖它的解析器。可以直接在路由配置里传递静态参数,或者用全局的路由守卫/解析器(放到CoreModule里)来处理数据,让懒加载模块之间通过路由参数间接通信,彻底切断直接依赖。
比如原来依赖其他模块解析的路由数据,改成直接在路由里配置:
// app-routing.module.ts const routes: Routes = [ { path: 'message', loadChildren: './features/message/message.module#MessageModule', data: { requiredData: 'xxx' } // 直接传递需要的数据 }, // 其他懒加载路由 ];
然后在message模块里通过ActivatedRoute获取data即可,完全不需要依赖其他懒加载模块。
3. 排查并消除循环依赖
有时候懒加载模块之间的依赖是隐性的循环依赖(比如A依赖B,B又依赖A),这种情况AOT绝对会报错。你可以用Angular CLI的命令ng lint来检测循环依赖,或者手动检查每个懒加载模块的import语句,确保没有导入其他懒加载模块。
4. 临时妥协:调整AOT编译配置(不推荐)
如果你暂时没时间重构代码,可以尝试调整tsconfig.json里的Angular编译选项,降低AOT的严格度,但这只是权宜之计,可能会带来其他隐患:
{ "angularCompilerOptions": { "strictMetadataEmit": false, "fullTemplateTypeCheck": false } }
这个方法能跳过一些元数据检查,但会失去AOT的部分类型校验优势,不建议长期使用。
内容的提问来源于stack exchange,提问作者user390749

