同样的,将当前模块的路由配置移动到专门的路由配置文件 crisis-routing.module.ts 中,并将 app-routing.module.ts 中相关的路由配置删除
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; // 引入组件 import { CrisisListComponent } from './crisis-list/crisis-list.component'; import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; // 引入路由守卫 import { AuthGuard } from '../auth/auth.guard'; const routes: Routes = [{ path: '', component: CrisisListComponent, canActivate: [AuthGuard], // 添加针对当前路由的 canActivate 路由守卫 children: [{ path: '', canActivateChild: [AuthGuard], // 添加针对子路由的 canActivate 路由守卫 children: [{ path: 'detail', component: CrisisDetailComponent }] }] }]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) export class CrisisRoutingModule { }重新运行项目,如果你在创建模块的命令中设置了自动引入当前模块到 app.module.ts 文件中,大概率会遇到下面的问题
这里的问题与配置通配路由需要放到最后的原因相似,因为脚手架在帮我们将创建的模块导入到 app.module.ts 中时,是添加到整个数组的最后,同时因为我们已经将 crisis 模块的路由配置移动到专门的 crisis-routing.module.ts 中了,框架在进行路由匹配时会预先匹配上 app-routing.module.ts 中设置的通配路由,从而导致无法找到实际应该对应的组件,因此这里我们需要将 AppRoutingModule 放到声明的最后
当问题解决后,就可以针对 crisis 模块设置惰性加载
在配置惰性路由时,我们需要以一种类似于子路由的方式进行配置,通过路由的 loadChildren 属性来加载对应的模块,而不是具体的组件,修改后的 AppRoutingModule 代码如下
import { HeroCanDeactivateGuard } from './hero-list/guards/hero-can-deactivate.guard'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: 'crisis-center', loadChildren: () => import('./crisis/crisis.module').then(m => m.CrisisModule) } ]; @NgModule({ imports: [RouterModule.forRoot(routes, { enableTracing: true })], exports: [RouterModule], }) export class AppRoutingModule { }当导航到这个 /crisis-center 路由时,框架会通过 loadChildren 字符串来动态加载 CrisisModule,然后把 CrisisModule 添加到当前的路由配置中,而惰性加载和重新配置工作只会发生一次,也就是在该路由首次被请求时执行,在后续请求时,该模块和路由都是立即可用的
4.3.2、CanLoad:杜绝未通过认证授权的组件加载在上面的代码中,对于 CrisisModule 模块我们已经使用 CanActivate、CanActivateChild 路由守卫来进行路由的认证授权,但是当我们并没有权限访问该路由的权限,却依然点击了链接时,此时框架路由仍会加载该模块。为了杜绝这种授权未通过仍加载模块的问题发生,这里需要使用到 CanLoad 守卫
因为这里的判断逻辑与认证授权的逻辑相同,因此在 AuthGuard 中,继承 CanLoad 接口即可,修改后的 AuthGuard 代码如下
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router, CanActivateChild, CanLoad, Route, UrlSegment } from '@angular/router'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate, CanActivateChild, CanLoad { /** * ctor * @param router 路由 */ constructor(private router: Router) { } canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { // 判断是否有 token 信息 let token = localStorage.getItem('auth-token') || ''; if (token === '') { this.router.navigate(['/login']); return false; } // 判断是否可以访问当前连接 let url: string = state.url; if (token.indexOf('admin') !== -1 && url.indexOf('/crisis-center') !== -1) { return true; } this.router.navigate(['/login']); return false; } canActivateChild( childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> { let token = localStorage.getItem('auth-token') || ''; if (token === '') { this.router.navigate(['/login']); return false; } return token === 'admin-master'; } canLoad(route: Route, segments: UrlSegment[]): boolean | Observable<boolean> | Promise<boolean> { let token = localStorage.getItem('auth-token') || ''; if (token === '') { this.router.navigate(['/login']); return false; } let url = `/${route.path}`; if (token.indexOf('admin') !== -1 && url.indexOf('/crisis-center') !== -1) { return true; } } }