路由守卫
任何时候导航到任何地方往往不能满足业务需求,需要授权守护。返回值是boolean的三种形式
- Observable
- Promise
- boolean
往往同步守卫不是一个好的选择,阻塞的情况时常发生。如果返回true,导航继续,否则,导航终止,停留原地
The router supports multiple guard interfaces:
在分层路由的每个级别上,我们都可以设置多个守卫。 路由器会先按照从最深的子路由由下往上检查的顺序来检查CanDeactivate()和CanActivateChild()守卫。 然后它会按照从上到下的顺序检查CanActivate()守卫。 如果特性模块是异步加载的,在加载它之前还会检查CanLoad()守卫。 如果任何一个守卫返回false,其它尚未完成的守卫会被取消,这样整个导航就被取消了。
特性模块的授权验证
匿名用户会重定向到登录页,因为区分用户是否授权,所以创建在根目录下(auth-guard.ts)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { Injectable } from '@angular/core'; import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/rotuer'; @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { let url = state.url; return this.checkLogin(url); } checkLogin(url: string): boolean { if ('登录成功标识') { reutrn true; } this.router.navigate(['/login']); return false } }
|
ActivatedRouteSnapshot包含即将被激活的路由,RouterStateSnapshot包含即将到达的状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { AuthGuard } from '../aut-guard.service'; const routes : Routes = [ path: 'admin', component: AdminComponent, canActive: [AuthGuard], ... ] @NgModule({ providers: [ AuthGuard ], ''' }) ...
|
CanActivateChild区别在于子路由被激活前的守卫
1 2 3 4
| import { CanActivateChild } from '@angular/router'; canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.canActivate(route, state); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const routes : Routes = [ path: 'admin', component: AdminComponent, canActive: [AuthGuard], children: [ { path: '', canActivateChild: [AuthGuard], children: [ ... ] } ] ] ...
|
CanDeactivate 处理未保存的更改;不保存并离开(true),保留更改并留下(false)
1 2 3 4 5 6
| canDeactivate(): Observable<boolean> | promise<boolean> | boolean { if (!this.changeStatus) { return true; //如果没有改变直接导航,否则弹框 } return this.someService.confirm('discard changes?'); }
|
1 2 3 4 5 6 7 8 9 10 11
| { path: ':id', component: CrisisDetailComponent, canDeactivate: [CanDeactivateGuard] } @NgModule({ providers: [ CanDeactivateGuard ], ''' })
|
Resolve 预先获取数据
如果响应时间够长,就需要预先从服务器上获取数据,路由激活瞬间数据渲染完毕,此处需要Resolve守卫。
1 2 3 4 5 6 7 8 9 10 11 12
| resolve(route: activatedRouteSnapshot, state: RouterStateSnapshot):Observable<any>{ const id = route.paramMap.get('id'); return this.someService.getDate(id).map(x => { if (x) { return x; }else { this.router.naviagte(['./someWhere']); return null; } }) } // 此处可以阻止路由被加载,直到数据获取完毕,如果没有数据获取则导航回指定路由,并且NgModule的providers中需要注册
|
异步路由
异步路由可以在获取请求时惰性加载特性模块,并且带来了一下好处
- 对于体积庞大的特性模块可以在用户请求时进行加载
- 持续扩充特性模块的功能,不用增加初始加载的体积及速度
- 模块化开发提升开发效率,结构分明
1 2 3 4
| { path: 'admin', loadChildren: 'app/admin/admin.module#AdminModule' // 相对于app目录 }
|
惰性加载只会发生一次,在该路由首次被请求时,后续的请求是立即可用的
Canload守卫 保护对特性模块的未授权加载
1 2 3 4
| canload(route: Route): boolean { let url = `${route.path}`; // route为准备访问的目标地址 return this.checkLogin(url); }
|
1 2 3 4 5
| { path: 'admin', loadChildren: 'app/admin/admin.module#AdminModule', canLoad: [AuthGuard] }
|
路由加载形式:立即加载、惰性加载、预加载
预加载好比后台加载,在保证了尽可能小的初始加载体积和首屏加载速度的同时,同样满足了特性模块的按需加载;理想情况下,首屏加载完毕后会有一个短暂的空档期,如果此时完成了接下来将要访问的模块的加载成功,体验会有很大提高。此时的加载就是预加载。
原理
导航完成后,路由器会查找没有加载但是可以加载的模块,此时预加载策略决定了是否加载以及加载哪些模块。
Router内置了两种预加载策略
- 完全不预加载,默认
- 预加载所有特性模块: PreloadAllModules
1 2 3 4
| import { PreloadAllModules, RouterModule } from '@angular/router' RouterModule.forRoot(appRoutes, { preloadingStrategy: PreloadAllModules })
|
CanLoad守卫会阻塞预加载策略,优先级高于预加载策略
自定义预加载策略
结合路由定义时的data属性,只预加载preload为true的路由
1 2 3 4 5
| { path: 'admin', loadChildren: 'app/admin/admin.module#AdminModule', data: { preload: true } }
|
添加selective-preloading-strategy.ts,实现自定义预加载策略
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { PreloadingStrategy , Route } from '@angular/router'; @Injectable() export class SelectivePreloadingStrategy implements PreloadingStrategy { preloadModules: string[] = []; preload(route: Route, load: () => Observable<any>) : Observable<any> { if (route.data && route.data['preload']) { this.preloadModules.push(route.path); return load(); } else { return Observable.of(null); } } }
|
如果要进行预加载,返回一个靠用加载器的Observable,否则返回一个null值的Observable对象。
重定向迁移URL
1
| { path: 'heroe/:id', redirectTo: '/superHero/:id'}
|
1
| RouterModule.forRoot(rotues, { useHash: true }) // 基于HashLocationStrategy
|