同一URL ID下加载不同模块的竞态问题解决方案咨询
Angular路由动态加载模块竞态条件解决方法
问题本质
当前路由工厂在模块初始化阶段就执行了stepsFacade.getVal(),但此时typeThatsNeedToBeLoaded还未通过getRouterState获取到当前URL对应的step-id的type值,导致加载错误模块,这就是竞态条件的根源。
方案一:动态获取路由参数并加载模块
把loadChildren改成异步函数,每次路由触发时才执行参数获取和模块加载逻辑,彻底避免初始化时的竞态。
修改路由工厂代码
function routesFactory(stepsFacade: StepsFacade) { return { path: '', component: StepsComponent, children: [ { path: ':id', loadChildren: async (route: ActivatedRouteSnapshot) => { // 获取当前路由的step-id参数 const stepId = route.paramMap.get('id'); // 通过stepId获取对应的type(需在StepsFacade中实现该异步方法) const type = await stepsFacade.getTypeById(stepId); // 从映射表中获取模块加载函数并执行 const loadModule = stepsFacade.stepsMapper[type]; if (!loadModule) { throw new Error(`未找到对应类型的模块: ${type}`); } return loadModule(); }, }, ], }; }
补充StepsFacade方法
// 添加根据stepId获取type的异步逻辑 async getTypeById(stepId: string): Promise<string> { const step = await this.getRouterState(stepId); // 调用原有获取step对象的方法 return step.type; } // 修正映射表的笔误(原ModuleC对应ModuleD,改为对应ModuleC) stepsMapper = { 'ModuleA': async () => (await import('@app/web/modules')).ModuleA, 'ModuleB': async () => (await import('@app/web/modules')).ModuleB, 'ModuleC': async () => (await import('@app/web/modules')).ModuleC, };
方案二:用路由守卫提前准备数据
通过Resolve守卫在路由激活前,提前获取对应type并存储到StepsFacade中,确保路由加载时typeThatsNeedToBeLoaded是正确值。
1. 创建Resolve守卫
@Injectable({ providedIn: 'root' }) export class StepTypeResolver implements Resolve<string> { constructor(private stepsFacade: StepsFacade) {} async resolve(route: ActivatedRouteSnapshot): Promise<string> { const stepId = route.paramMap.get('id'); const type = await this.stepsFacade.getTypeById(stepId); this.stepsFacade.setTypeThatsNeedToBeLoaded(type); // 将type存入facade return type; } }
2. 修改路由配置
function routesFactory(stepsFacade: StepsFacade) { return { path: '', component: StepsComponent, children: [ { path: ':id', resolve: { stepType: StepTypeResolver }, loadChildren: () => stepsFacade.getVal(), }, ], }; }
3. 补充StepsFacade的set方法
setTypeThatsNeedToBeLoaded(type: string): void { this.typeThatsNeedToBeLoaded = type; }
方案对比
- 方案一:逻辑直接,每次导航动态处理,完全规避竞态,适合绝大多数场景。
- 方案二:通过守卫提前完成数据准备,适合需要在路由激活前做额外数据校验或预处理的复杂场景,但多了一层守卫逻辑。
内容的提问来源于stack exchange,提问作者michalgrzegorczyk-dev
相关产品推荐
相关产品推荐

