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 相关文章推荐
在网页中使用document.write时遭遇的奇怪问题
Aug 24 Javascript
Array的push与unshift方法性能比较分析
Mar 05 Javascript
对frameset、frame、iframe的js操作示例代码
Aug 16 Javascript
js中回调函数的学习笔记
Jul 31 Javascript
node.js中的fs.futimesSync方法使用说明
Dec 17 Javascript
js实现数字每三位加逗号的方法
Feb 05 Javascript
js使用post 方式打开新窗口
Feb 26 Javascript
JavaScript操作表单实例讲解(上)
Jun 20 Javascript
JS组件系列之Gojs组件 前端图形化插件之利器
Nov 29 Javascript
详解小程序云开发数据库
May 20 Javascript
js+canvas实现纸牌游戏
Mar 16 Javascript
详解webpack-dev-middleware 源码解读
Mar 23 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新手谈谈我的学习心得
2007/02/25 PHP
利用php下载xls文件(自己动手写的)
2014/04/18 PHP
thinkphp模板继承实例简述
2014/11/26 PHP
WordPress后台中实现图片上传功能的实例讲解
2016/01/11 PHP
js jquery获取随机生成id的服务器控件的三种方法
2013/07/11 Javascript
javascript实现回车键提交表单方法总结
2015/01/10 Javascript
JavaScript时间转换处理函数
2015/04/14 Javascript
JavaScript实现自动生成网页元素功能(按钮、文本等)
2015/11/21 Javascript
理解js回收机制通俗易懂版
2016/02/29 Javascript
Vuejs第六篇之Vuejs与form元素实例解析
2016/09/05 Javascript
Js获取当前日期时间及格式化代码
2016/09/17 Javascript
AngularJS实现使用路由切换视图的方法
2017/01/24 Javascript
nodejs个人博客开发第一步 准备工作
2017/04/12 NodeJs
微信小程序tabbar不显示解决办法
2017/06/08 Javascript
jquery点击回车键实现登录效果并默认焦点的方法
2018/03/09 jQuery
JS实现数组去重及数组内对象去重功能示例
2019/02/02 Javascript
详解Vue webapp项目通过HBulider打包原生APP(vue+webpack+HBulider)
2019/02/02 Javascript
node学习笔记之读写文件与开启第一个web服务器操作示例
2019/05/29 Javascript
python启动办公软件进程(word、excel、ppt、以及wps的et、wps、wpp)
2009/04/09 Python
30分钟搭建Python的Flask框架并在上面编写第一个应用
2015/03/30 Python
将Python代码打包为jar软件的简单方法
2015/08/04 Python
python对于requests的封装方法详解
2019/01/03 Python
使用python实现数组、链表、队列、栈的方法
2019/12/20 Python
django之从html页面表单获取输入的数据实例
2020/03/16 Python
调整Jupyter notebook的启动目录操作
2020/04/10 Python
详解px单位html5响应式方案
2018/03/08 HTML / CSS
美国球鞋寄卖网站:Stadium Goods
2018/05/09 全球购物
Nordgreen英国官网:斯堪的纳维亚设计师手表
2018/10/24 全球购物
英国门把手公司:Door Handle Company
2019/05/12 全球购物
乌克兰巴士票购买网站:inBus
2021/03/12 全球购物
总经理职责
2013/12/22 职场文书
2014年个人业务工作总结
2014/11/17 职场文书
2015年党员干部承诺书
2015/01/21 职场文书
九寨沟导游词
2015/02/02 职场文书
自信主题班会
2015/08/14 职场文书
导游词之宁夏贺兰山岩画
2019/11/08 职场文书