Angular Router级联重定向问题:二次重定向未触发
你遇到的这个问题我之前也碰过,本质是Angular Router在处理CanLoad守卫时的机制限制——当CanLoad阻止了模块加载时,目标路由的内部配置根本没机会被解析,所以二次重定向自然不会触发。
先把你的场景补全一下,方便理解:假设你的根路由配置是这样的:
const routes: Routes = [ { path: '', redirectTo: 'account', pathMatch: 'full' }, { path: 'account', loadChildren: './modules/account/account.module#AccountModule', canLoad: [CanLoadAccountGuard] }, ];
而AccountModule内部还有一层重定向:
const accountRoutes: Routes = [ { path: '', redirectTo: 'login', pathMatch: 'full' }, { path: 'login', component: LoginComponent } ];
当你访问根路径/时,第一次重定向到/account没问题,但因为CanLoad守卫不允许加载AccountModule,Angular根本读不到里面的redirectTo: 'login'规则,所以二次重定向就卡壳了。
下面给你几个可行的解决方案,你可以根据业务需求选择:
方案1:直接把最终重定向目标写到根路由
如果业务逻辑允许,最简单的办法就是跳过中间的account重定向,直接在根路由里指向最终的登录页:
const routes: Routes = [ { path: '', redirectTo: 'account/login', pathMatch: 'full' }, { path: 'account', loadChildren: './modules/account/account.module#AccountModule', canLoad: [CanLoadAccountGuard] }, ];
这样不管CanLoad是否允许加载模块,第一次重定向都会直接到/account/login,如果模块已加载就会命中Login组件;如果没加载,你可以额外配置一个404路由或者让守卫处理这个路径的跳转。
方案2:在CanLoad守卫里手动触发重定向
如果必须保留原有的路由结构,那可以在守卫里直接处理跳转,不用依赖路由的自动重定向逻辑:
@Injectable() export class CanLoadAccountGuard implements CanLoad { constructor(private router: Router) {} canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean { // 这里写你的权限判断逻辑,比如判断用户是否登录 const hasAccess = false; if (!hasAccess) { // 直接跳转到登录页,绕过中间的account路由 this.router.navigate(['/account/login']); return false; } return true; } }
这样当守卫阻止模块加载时,会直接跳转到最终的目标页面,完美解决二次重定向失效的问题。
方案3:换成CanActivate守卫(如果不需要阻止模块加载)
如果你的业务不需要严格阻止模块加载,只是要限制路由访问,那可以把CanLoad换成CanActivate守卫。这样AccountModule会先被加载,内部的重定向规则自然就会生效:
// 根路由配置修改 const routes: Routes = [ { path: '', redirectTo: 'account', pathMatch: 'full' }, { path: 'account', loadChildren: './modules/account/account.module#AccountModule', canActivate: [CanActivateAccountGuard] }, ];
不过要注意,CanActivate不会阻止模块的下载,只是阻止路由的激活,所以如果你的核心需求是必须延迟加载并阻止未授权用户下载模块,这个方案就不适用了。
总的来说,这个问题的核心就是CanLoad守卫阻止了模块加载,导致目标路由的内部配置无法被解析。根据你的实际业务场景选上面的方案就可以解决啦。
内容的提问来源于stack exchange,提问作者Bernie

