0%

路由守卫及加载策略

路由守卫

任何时候导航到任何地方往往不能满足业务需求,需要授权守护。返回值是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