Angular8路由守卫原理和使用方法


Posted in Javascript onAugust 29, 2019

路由守卫

守卫,顾名思义,必须满足一定的条件得到许可方可通行,否则拒绝访问或者重定向。Angular中路由守卫可以借此处理一些权限问题,通常应用中存储了用户登录和用户权限信息,遇到路由导航时会进行验证是否可以跳转。

4种守卫类型

按照触发顺序依次为:canload(加载)、canActivate(进入)、canActivateChild(进入子路由)和canDeactivate(离开)。

一个所有守卫都是通过的守卫类:

import { Injectable } from '@angular/core';
import {
 CanActivate,
 Router,
 ActivatedRouteSnapshot,
 RouterStateSnapshot,
 CanActivateChild,
 CanLoad,
 CanDeactivate
} from '@angular/router';
import { Route } from '@angular/compiler/src/core';
import { NewsComponent } from '../component/news/news.component';


@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad, CanDeactivate<any> {
 constructor(
  private router: Router
 ) {

 }
 canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
  // 权限控制逻辑如 是否登录/拥有访问权限
  console.log('canActivate');
  return true;
 }
 canDeactivate(
  component: NewsComponent,
  currentRoute: ActivatedRouteSnapshot,
  currentState: RouterStateSnapshot,
  nextState: RouterStateSnapshot) {
  console.log('canDeactivate');
  return true;
 }
 canActivateChild() {
  // 返回false则导航将失败/取消
  // 也可以写入具体的业务逻辑
  console.log('canActivateChild');
  return true;
 }
 canLoad(route: Route) {
  // 是否可以加载路由
  console.log('canload');
  return true;
 }
}

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ErrorComponent } from './error/error.component';
import { AuthGuard } from './core/auth-guard';

const routes: Routes = [
 // 一般情况很少需要同时写多个守卫,如果有也是分开几个文件(针对复杂场景,否则一般使用canActivated足够)
 {
  path: '',
  canLoad: [AuthGuard],
  canActivate: [AuthGuard],
  canActivateChild: [
   AuthGuard
  ],
  canDeactivate: [AuthGuard],
  loadChildren: () => import('./pages/pages.module').then(m => m.PagesModule)
 },
 {
  path: 'error',
  component: ErrorComponent,
  data: {
   title: '参数错误或者地址不存在'
  }
 },
 {
  path: '**',
  redirectTo: 'error',
  pathMatch: 'full'
 }
];

@NgModule({
 imports: [RouterModule.forRoot(routes)],
 exports: [RouterModule]
})
export class AppRoutingModule { }

使用场景分析

1.canLoad

默认值为true,表明路由是否可以被加载,一般不会认为控制这个守卫逻辑,99.99%情况下,默认所有app模块下路由均允许canLoad

2.canActivate

是否允许进入该路由,此场景多为权限限制的情况下,比如客户未登录的情况下查询某些资料页面,在此方法中去判断客户是否登陆,如未登录则强制导航到登陆页或者提示无权限,即将返回等信息提示。

3.canActivateChild

是否可以导航子路由,同一个路由不会同时设置canActivate为true,canActivateChild为false的情况,此外,这个使用场景很苛刻,尤其是懒加载路由模式下,暂时未使用到设置为false的场景。

4.CanDeactivate

路由离开的时候进行触发的守卫,使用场景比较经典,通常是某些页面比如表单页面填写的内容需要保存,客户突然跳转其它页面或者浏览器点击后退等改变地址的操作,可以在守卫中增加弹窗提示用户正在试图离开当前页面,数据还未保存 等提示。

场景模拟

登录判断

前期准备:login组件;配置login路由

因为login是独立一个页面,所以app.component.html应该只会剩余一个路由导航

<!-- NG-ZORRO -->
<router-outlet></router-outlet>

取而代之的是pages.component.html页面中要加入header和footer部分变为如下:

<app-header></app-header>
<div nz-row class="main">
 <div nz-col nzSpan="24">
  <router-outlet></router-outlet>
 </div>
</div>
<app-footer></app-footer>

app-routing.module.ts 中路由配置2种模式分析:

// 非懒加载模式
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ErrorComponent } from './error/error.component';
import { AuthGuard } from './core/auth-guard';
import { LoginComponent } from './component/login/login.component';
import { PagesComponent } from './pages/pages.component';
import { IndexComponent } from './component/index/index.component';

const routes: Routes = [
 // 一般情况很少需要同时写多个守卫,如果有也是分开几个文件(针对复杂场景,否则一般使用canActivated足够)
 {
  path: '',
  canLoad: [AuthGuard],
  canActivate: [AuthGuard],
  canActivateChild: [
   AuthGuard
  ],
  canDeactivate: [AuthGuard],
  component: PagesComponent,
  children: [
   {
    path: 'index',
    component: IndexComponent
   }
   // ...
  ]
  // loadChildren: () => import('./pages/pages.module').then(m => m.PagesModule)
 },
 {
  path: 'login',
  component: LoginComponent,
  data: {
   title: '登录'
  }
 },
 {
  path: 'error',
  component: ErrorComponent,
  data: {
   title: '参数错误或者地址不存在'
  }
 },
 {
  path: '**',
  redirectTo: 'error',
  pathMatch: 'full'
 }
];

@NgModule({
 imports: [RouterModule.forRoot(routes)],
 exports: [RouterModule]
})
export class AppRoutingModule { }

非懒加载模式下,想要pages组件能够正常显示切换的路由和固定头部足部,路由只能像上述这样配置,也就是所有组件都在app模块中声明,显然不是很推荐这种模式,切换回懒加载模式:

{
  path: '',
  canLoad: [AuthGuard],
  canActivate: [AuthGuard],
  canActivateChild: [
   AuthGuard
  ],
  canDeactivate: [AuthGuard],
  loadChildren: () => import('./pages/pages.module').then(m => m.PagesModule)
 },

pages-routing.module.ts

初始模板:

const routes: Routes = [
 {
  path: '',
  redirectTo: 'index',
  pathMatch: 'full'
 },
 {
  path: 'index',
  component: IndexComponent,
  data: {
   title: '公司主页'
  }
 },
 {
  path: 'about',
  component: AboutComponent,
  data: {
   title: '关于我们'
  }
 },
 {
  path: 'contact',
  component: ContactComponent,
  data: {
   title: '联系我们'
  }
 },
 {
  path: 'news',
  canDeactivate: [AuthGuard],
  loadChildren: () => import('../component/news/news.module').then(m => m.NewsModule)
 },
]

浏览器截图:

Angular8路由守卫原理和使用方法

明明我们的html写了头部和底部组件却没显示?

路由的本质:根据配置的path路径去加载组件或者模块,此处我们是懒加载了路由,根据路由模块再去加载不同组件,唯独缺少了加载了pages组件,其实理解整个并不难,index.html中有个<app-root></app-root>,这就表明app组件被直接插入了dom中,反观pages组件,根本不存在直接插进dom的情况,所以这个组件根本没被加载,验证我们的猜想很简单:

export class PagesComponent implements OnInit {

 constructor() { }

 ngOnInit() {
  alert();
 }

}

经过刷新页面,alert()窗口并没有出现~,可想而知,直接通过路由模块去加载了对应组件;其实我们想要的效果就是之前改造前的app.component.html效果,所以路由配置要参照更改:

const routes: Routes = [
 {
  path: '',
  component: PagesComponent,
  children: [
   {
    path: '',
    redirectTo: 'index',
    pathMatch: 'full'
   },
   {
    path: 'index',
    component: IndexComponent,
    data: {
     title: '公司主页'
    }
   },
   {
    path: 'about',
    component: AboutComponent,
    data: {
     title: '关于我们'
    }
   },
   {
    path: 'contact',
    component: ContactComponent,
    data: {
     title: '联系我们'
    }
   },
   {
    path: 'news',
    canDeactivate: [AuthGuard],
    loadChildren: () => import('../component/news/news.module').then(m => m.NewsModule)
   },
  ]
 }
];

Angular8路由守卫原理和使用方法

这样写,pages组件就被加载了,重回正题,差点回不来,我们在登录组件中写了简单的登录逻辑:

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';

@Component({
 selector: 'app-login',
 templateUrl: './login.component.html',
 styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
 loginForm: FormGroup;
 constructor(
  private fb: FormBuilder,
  private router: Router
 ) { }

 ngOnInit() {
  this.loginForm = this.fb.group({
   loginName: ['', [Validators.required]],
   password: ['', [Validators.required]]
  });
  console.log(this.loginForm);
 }

 loginSubmit(event, value) {
  if (this.loginForm.valid) {
   window.localStorage.setItem('loginfo', JSON.stringify(this.loginForm.value));
   this.router.navigateByUrl('index');
  }
 }
}

守卫中:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
  // 权限控制逻辑如 是否登录/拥有访问权限
  console.log('canActivate', route);
  const isLogin = window.localStorage.getItem('loginfo') ? true : false;
  if (!isLogin) {
   console.log('login');
   this.router.navigateByUrl('login');
  }
  return true;
 }

Angular8路由守卫原理和使用方法

路由离开(选定应用的组件是contact组件):

canDeactivate(
  component: ContactComponent,
  currentRoute: ActivatedRouteSnapshot,
  currentState: RouterStateSnapshot,
  nextState: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  console.log('canDeactivate');
  return component.pageLeave();
 }
{
  path: 'contact',
  canDeactivate: [AuthGuard],
  component: ContactComponent,
  data: {
   title: '联系我们'
  }
 }
pageLeave(): Observable<boolean> {
  return new Observable(ob => {
   if (!this.isSaven) {
    this.modal.warning({
     nzTitle: '正在离开,是否需要保存改动的数据?',
     nzOnOk: () => {
      // 保存数据
      ob.next(false);
      alert('is saving');
      this.isSaven = true;
     },
     nzCancelText: '取消',
     nzOnCancel: () => {
      ob.next(true);
     }
    });
   } else {
    ob.next(true);
   }
  });
 }

默认数据状态时未保存,可以选择不保存直接跳转也可以保存之后再跳转。

Angular8路由守卫原理和使用方法

此场景多用于复杂表单页或者一些填写资料步骤的过程中,甚至浏览器后退和前进的操作也会触发这个守卫,唯一不足的地方时这个守卫绑定的是单一页面,无法统一对多个页面进行拦截。

下一篇介绍路由事件的运用。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
Js+XML 操作
Sep 20 Javascript
js对table的td进行相同内容合并示例详解
Dec 27 Javascript
javascript打开word文档的方法
Apr 16 Javascript
jQuery表单域属性过滤器用法分析
Feb 10 Javascript
jquery实现网页定位导航
Aug 23 Javascript
JavaScript登录验证基础教程
Nov 01 Javascript
vue单页面实现当前页面刷新或跳转时提示保存
Nov 02 Javascript
微信小程序实现点赞、取消点赞功能
Nov 02 Javascript
微信小程序点击保存图片到本机功能
Dec 13 Javascript
vue Element左侧无限级菜单实现
Jun 10 Javascript
在vue中实现echarts随窗体变化
Jul 27 Javascript
Angular+Ionic使用queryParams实现跳转页传值的方法
Sep 05 Javascript
Vue中的循环及修改差值表达式的方法
Aug 29 #Javascript
vue父子组件通信的高级用法示例
Aug 29 #Javascript
Elementui表格组件+sortablejs实现行拖拽排序的示例代码
Aug 28 #Javascript
JS中async/await实现异步调用的方法
Aug 28 #Javascript
基于Node的Axure文件在线预览的实现代码
Aug 28 #Javascript
详解用async/await来处理异步
Aug 28 #Javascript
vue中监听返回键问题
Aug 28 #Javascript
You might like
PHP版 汉字转码的实现详解
2013/06/09 PHP
PHP+jQuery实现双击修改table表格功能示例
2019/02/21 PHP
Backbone.js中的集合详解
2015/01/14 Javascript
js支持键盘控制的左右切换立体式图片轮播效果代码分享
2015/08/26 Javascript
jquery实现的Accordion折叠面板效果代码
2015/09/02 Javascript
原生js实现移动端瀑布流式代码示例
2015/12/18 Javascript
JavaScript判断DIV内容是否为空的方法
2016/01/29 Javascript
利用vue-router实现二级菜单内容转换
2016/11/30 Javascript
微信小程序开发之视频播放器 Video 弹幕 弹幕颜色自定义实例
2016/12/08 Javascript
Bootstrap select实现下拉框多选效果
2016/12/23 Javascript
纯原生js实现table表格的增删
2017/01/05 Javascript
微信端开发--登录小程序步骤
2017/01/11 Javascript
使用jQuery实现购物车结算功能
2017/08/15 jQuery
bootstrap日期插件daterangepicker使用详解
2017/10/19 Javascript
详解vue-cli脚手架中webpack配置方法
2018/08/22 Javascript
Vue路由模块化配置的完整步骤
2019/08/14 Javascript
nodejs dgram模块广播+组播的实现示例
2019/11/04 NodeJs
toString.call()通用的判断数据类型方法示例
2020/08/28 Javascript
如何使用原生Js实现随机点名详解
2021/01/06 Javascript
[38:54]完美世界DOTA2联赛PWL S2 Rebirth vs LBZS 第一场 11.28
2020/12/01 DOTA
利用python发送和接收邮件
2016/09/27 Python
python如何拆分含有多种分隔符的字符串
2018/03/20 Python
对python 读取线的shp文件实例详解
2018/12/22 Python
Python弹出输入框并获取输入值的实例
2019/06/18 Python
使用TensorFlow直接获取处理MNIST数据方式
2020/02/10 Python
jupyter notebook tensorflow打印device信息实例
2020/04/20 Python
基于python实现操作redis及消息队列
2020/08/27 Python
从零开始的TensorFlow+VScode开发环境搭建的步骤(图文)
2020/08/31 Python
台湾屈臣氏网路商店:Watsons台湾
2020/12/29 全球购物
触摸春天教学反思
2014/02/03 职场文书
小学运动会入场式解说词
2014/02/18 职场文书
给校长的建议书300字
2014/05/16 职场文书
节水标语大全
2014/06/11 职场文书
幼儿园大班开学寄语
2014/08/02 职场文书
辅导员学期工作总结
2015/08/14 职场文书
2019七夕节祝福语36句,快来收藏吧
2019/08/06 职场文书