热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Angular8路由守卫原理与使用

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

路由守卫

守卫,顾名思义,必须满足一定的条件得到许可方可通行,否则拒绝访问或者重定向。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 {
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应该只会剩余一个路由导航


取而代之的是pages.component.html页面中要加入header和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组件被直接插入了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 | Promise | boolean {
console.log('canDeactivate');
return component.pageLeave();
}

{
path: 'contact',
canDeactivate: [AuthGuard],
component: ContactComponent,
data: {
title: '联系我们'
}
}

pageLeave(): Observable {
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 路由守卫原理与使用》
此场景多用于复杂表单页或者一些填写资料步骤的过程中,甚至浏览器后退和前进的操作也会触发这个守卫,唯一不足的地方时这个守卫绑定的是单一页面,无法统一对多个页面进行拦截。

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


推荐阅读
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • 本文介绍了如何使用vue-awesome-swiper组件,包括在main.js中引入和使用swiper和swiperSlide组件,以及设置options和ref属性。同时还介绍了如何在模板中使用swiper和swiperSlide组件,并展示了如何通过循环渲染swipes数组中的数据,并使用picUrl属性显示图片。最后还介绍了如何添加分页器。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • 本文讨论了将HashRouter改为Router后,页面全部变为空白页且没有报错的问题。作者提到了在实际部署中需要在服务端进行配置以避免刷新404的问题,并分享了route/index.js中hash模式的配置。文章还提到了在vueJs项目中遇到过类似的问题。 ... [详细]
  • 本文讨论了在使用Git进行版本控制时,如何提供类似CVS中自动增加版本号的功能。作者介绍了Git中的其他版本表示方式,如git describe命令,并提供了使用这些表示方式来确定文件更新情况的示例。此外,文章还介绍了启用$Id:$功能的方法,并讨论了一些开发者在使用Git时的需求和使用场景。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 本文讨论了为什么在main.js中写import不会全局生效的问题,并提供了解决方案。在每一个vue文件中都需要写import语句才能使其生效,而在main.js中写import语句则不会全局生效。本文还介绍了使用Swal和sweetalert2库的示例。 ... [详细]
  • 怀疑是每次都在新建文件,具体代码如下 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 使用圣杯布局模式实现网站首页的内容布局
    本文介绍了使用圣杯布局模式实现网站首页的内容布局的方法,包括HTML部分代码和实例。同时还提供了公司新闻、最新产品、关于我们、联系我们等页面的布局示例。商品展示区包括了车里子和农家生态土鸡蛋等产品的价格信息。 ... [详细]
  • 前段时间做一个项目,需求是对每个视频添加预览图,这个问题最终选择方案是:用canvas.toDataYRL();来做转换获取视频的一个截图,添加到页面中,达到自动添加预览图的目的。 ... [详细]
author-avatar
亲爱的常青藤先生
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有