angular11源码探索二十二[路由Route下路由守卫]
路由守卫
如果类似于
//当前的导航并开始一个新的
//路由器将自动取消,因为重定向一个新路由,也不一定非要返回false
this.router.navigateByUrl('/home/d')
navigateByUrl激活守卫
providers:[ // 添加到路由上的守卫
{
provide:'can1',
useValue:(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)=>{
return false
}
]
this.router.navigateByUrl('/home/d/c;p=1').then(v=>{
console.log(v); // 当否是成功激活的时候返回true/false
})
canActivate
用于确定是否可以激活路由。如果所有守卫都返回true
,导航将继续。如果有任何后卫返回false
,导航将被取消。
CanActivateChild
类可以实现为确定是否可以激活子路由的后卫的接口。如果所有守卫都返回true
,导航将继续。如果有任何后卫返回false
,导航将被取消。如果任何防护返回a UrlTree
,则当前导航被取消,并且新的导航开始UrlTree
从防护返回。
{
path: 'a', component: AComponent, data: {name: 'xxx'},
canActivate: [OneService],
canActivateChild:[OneService],
children: [
{path:'c/d', component: CComponent}
]
},
export class OneService implements CanActivate, CanActivateChild {
constructor() {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
: Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
// route 当前快照
console.log(route);
// 当前的url
// console.log(state.url);
return true;
}
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
// childRoute 路由的子快照
console.log(childRoute);
return true;
}
}
canload 防止异步守卫
**canActivate**
存在这种情况是为了防止未经授权的用户访问路由,而**canLoad**
用于防止应用程序在未经授权的情况下以延迟方式(延迟加载)加载整个模块或组件
{path: 'home', loadChildren: () =>
import('./three/three.module').then(m => m.ThreeModule),
canLoad: [TestService]
},
@Injectable()
export class TestService implements Resolve<any>,CanActivate,CanDeactivate<any>,CanLoad{
canDeactivate(component: any, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return of(true)
}
canload 只会触发一次
{ path: 'hello',
loadChildren: () => import('./five/five.module').then(m => m.FiveModule),
canLoad:['con1']
},
providers: [
{provide:'con1',
useValue: (a: ActivatedRouteSnapshot, s: RouterStateSnapshot)
=> {
console.log(1111);
return true
}
}
],
我们发现因为默认是没有执行的,所以只执行一次
通过我们发现这么模块也是只第一次的时候创建,所以慢慢就理解了constructor
的用处了吧
我们发现当拦截者第一次进入的时候才会执行,第二次就不会执行啦
模块
export class TwoRoutingModule implements OnInit{
constructor() {
console.log('xxx');
}
}
canload 和canActivate之间的区别
触发顺序:
-canload 加载
-canActivate 进入(重要)
这两种不是写在一起,所以顺序应该不能一概而论
canActivateChild 进入子路由
canDeactivate 离开(重要)
canActivate 用于防止未经授权的用户访问某些路由
canLoad 用于防止应用程序(延迟加载)整个模块
传参
参数依赖注入令牌数组
竟然可以这样用
模块中
@NgModule({
...
providers: [
{
provide: 'CanActivate1',
useValue: (a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => {
return of(false)
}
},
{
provide: 'CanActivate2',
useValue: (a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => {
if (a.params['id'] === '22') {
return Promise.resolve(true);
} else {
return Promise.resolve(false);
}
}
},
// 跳转到新路由,为什么多包一层,因为避免调用的时候默认调用
{
provide:'can2',
useFactory:(router:Router)=>()=>{
console.log(router.url);
router.navigateByUrl('/home/d/1')
return false;
},
deps:[Router]
},
// 返回UrlTree类型,跳转到新路由
{
provide:'can3',
useFactory:(router:Router)=>()=>{
console.log(router.url);
return router.parseUrl('/home/d/1')
},
deps:[Router]
}
]
})
路由拦截
const routes: Routes = [{
path: '', component: OneComponent, children: [
{path: 'a', component: AComponent,
canActivate:['CanActivate1']// 竟然可以这样写,学习到了
},
]
}];
resolve(解决守卫)
保证了数据获取后在进行路由跳转,防止因为数据延迟而出现的空组件情况
简单的理解成解决延迟守卫
{
path: 'b', component: BComponent,
resolve: {sex: TestService}
},
=====
@Injectable()
export class TestService implements Resolve<any>{
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any {
return of([1,2,3])
}
}
=====
export class BComponent implements OnInit, AfterViewInit {
constructor(
private route:ActivatedRoute
) {
this.route.data.subscribe(console.log);
// {sex: [1, 2, 3]}
}
}
路由防护策略
先上源码
export type RunGuardsAndResolvers = 'pathParamsChange'|'pathParamsOrQueryParamsChange'|'paramsChange'|'paramsOrQueryParamsChange'|
'always'|((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);
最后一个是自定义函数,返回boolean,用来判断是否促发守卫
runGuardsAndResolvers
的选项,默认paramsChange
模式
意味着他将重新运行路径或路径参数的变化,会触发变更
变更: 意思是会触发路由守卫
{
path: 'home/:id',
component: HomeComponent,
...
runGuardsAndResolvers: 'paramsChange'
}
执行
/home/1 => /home/2
/home/1 => /home/1;param1=38
/home/1;param1=38 => /home/1;param1=20
不执行
/home/1 => /home/1?queryParam=hi
为了方便理解,我写一个小案例
{
path: 'a', component: AComponent,
children: [
{
path: 'c/:id', component: CComponent, canActivate: [OneService],
}
]
},
====
export class OneService implements CanActivate, CanActivateChild {
constructor() {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
: Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
// route 当前快照
console.log('触发');
// 当前的url
// console.log(state.url);
return true;
}
}
====
<a routerLink="/home/a/c/1">c/1</a> <br>
<a routerLink="/home/a/c/2">c/2</a> <br>
<a routerLink="/home/a/c/3">c/3</a> <br>
<a routerLink="/home/a/c/3" [queryParams]="{name:'xxx'}">c/3?name=xxx</a> <br>
<a routerLink="/home/a/c/3" [queryParams]="{name:'bbb'}">c/3?name=bbb</a> <br>
<a routerLink="/home/a/c/3" [queryParams]="{name:'ccc'}">c/3?name=ccc</a> <br>
我们会发现问号传参的,默认路由防护策略不会执行路由守卫
paramsOrQueryParamsChange
任何参数更改时。这包括路径,矩阵和查询参数。那么什么参数发生变化,不会引起参数的变化
改变矩阵参数不会导致触发防护
上代码(多出口的切换)
{
path: 'd', component: DComponent,
runGuardsAndResolvers:'paramsOrQueryParamsChange',
canActivate:['can3'],
children:[
{path: 'c', component: CComponent,},
{path: 'a', component: AComponent,outlet:'right'},
]
},
// 测试守卫拦截
{
provide:'can3',
useFactory:(router:Router)=>()=>{
console.log(1);
return true
},
deps:[Router]
}
我们写两个方法让他们切换,我们发现当点击第一个会触发,切换到第二个不会触发
backs() {
this.router.navigateByUrl('/home/d/c')
}
clickMethod() {
this.router.navigateByUrl('/home/d/(c//right:a)')
}
========
this.router.navigateByUrl('/home/d/c;p=1')
this.router.navigateByUrl('/home/d/c;p=2')
总结参数
-
paramsChange :当路径修改忽略查询参数更改,忽略新出口的更改
-
pathParamsChange : 将忽略矩阵参数或查询参数或新出口的更改或者#号,并在路径或路径参数更改时重新运行,父路由参数的更改,也会触发(父路由应该算路径或路径参数)
-
paramsOrQueryParamsChange : 在原来的基础上加了查询参数和#。,忽略新出口的更改,忽略改变矩阵参数更改
-
always : 设置为“始终”时,它们将在每次导航中运行
-
函数: 自定义触发,返回boolean
paramsChange 默认忽略查询参数更改,忽略新出口的更改,忽略描点,忽略多入口 /a 改变 /a;p=1 /a;p=2 pathParamsChange 原来的基础上忽略最后的矩阵参数 不变 /a;p=1 /a;p=2 变化 /d/1;dd=11/e/2;dd=22 /d/1;dd=11/e/3;ee=22 内部的矩阵参数还是检测到变化的 paramsOrQueryParamsChange 查询参数和描点修改会触发 修改 /a;p=1 /a;p=2 /a;p=2?q=1 pathParamsOrQueryParamsChange 忽略矩阵的修改,检测问号传参 不会 /a;p=1 /a;p=2 会 /a;p=2?q=1 always 修改始终触发 自定义函数触发 runGuardsAndResolvers:(from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot)=>{ console.log(from, to); return true; },
空路由后面没有子路由
{
path: 'team/:id',
component: TeamCmp,
children: [
// 正确使用方式 pathMatch: 'full'
{path: '', pathMatch: 'full', component: SimpleCmp},
{path: 'user/:name', component: UserCmp, canDeactivate: ['CanDeactivateUser']}
]
}
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者
总有一天我也能成为大佬