Angular guard 路由守卫的简介

3,069 阅读3分钟

啥时候会用到路由守卫?

  1. 该用户可能无权导航到目标组件。
  2. 可能用户得先登录(认证)。
  3. 在显示目标组件前,你可能得先获取某些数据。
  4. 在离开组件前,你可能要先保存修改。
  5. 你可能要询问用户:你是否要放弃本次更改,而不用保存它们?

路由守卫相关的接口

  1. 用CanActivate来处理导航到某路由的情况。 =》 进入路由的时候触发
  2. 用CanActivateChild来处理导航到某子路由的情况。 =》 进入路由的时候触发
  3. 用CanDeactivate来处理从当前路由离开的情况。 =》 离开路由的时候触发
  4. 用Resolve在路由激活之前获取路由数据。
  5. 用CanLoad来处理异步导航到某特性模块的情况。

接下来通过具体的实例介绍如何使用CanActivate,CanActivateChild,CanDeactivate😺

CanActivate与CanActivateChild

守卫可以用同步的方式返回一个布尔值。但在很多情况下,守卫无法用同步的方式给出答案。 守卫可能会向用户问一个问题、把更改保存到服务器,或者获取新数据,而这些都是异步操作。 因此,路由的守卫可以返回一个 Observable 或 Promise,并且路由器会等待这个可观察对象被解析为 true 或 false。 所以接下来的代码我们会用两个版本分别试验同步和异步的写法。

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, CanActivateChild, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { UserService } from '../../../../common/services/user.service';

@Injectable()
export class ExampleGuardService implements CanActivate, CanActivateChild {

 // version1.0 异步版

  public isBecomeUser$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(null);
  constructor(private router: Router, private userService: UserService) {
    this.verifyAccess();
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.isBecomeUser$.filter(status => typeof status === 'boolean');
  }

  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.isBecomeUser$.filter(status => typeof status === 'boolean');
  }

  private verifyAccess() {
     // 这里是用来返回isBecomeUser$的值
     // 如果它返回 true,导航过程会继续
     // 如果它返回 false,导航过程就会终止,且用户留在原地。
     // 守卫还可以告诉路由器导航到别处,这样也会取消当前的导航。要想在守卫中这么做,就要返回 false;
  }
  
 // version2.0 同步版
 
  public canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    const data = next.data;
    const {authName} = data;
    if (authName) {
      return this.checkAuth(authName);
    }
    return true;
  }

  public canActivateChild(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
   // CanActivateChild 守卫和 CanActivate 守卫很像。 它们的区别在于,CanActivateChild 会在任何子路由被激活之前运。
   // 所以他们的判断逻辑是相同的只是使用的地方不同。
   // 如果要保护管理特性模块
      防止它被非授权访问,还要保护这个特性模块内部的那些子路由
      你可以使用canActivateChild
    return this.canActivate(next, state);
  }

  public checkAuth(authName: string): boolean { // 判断是否可以进入页面的相关逻辑
    const hasAuth = this.example.isAuth(authName);
    if (hasAuth) {
      return true;
    }
    this.router.navigate(['/403']);
    return false;
  }
}

 // CanActivate, CanActivateChild如何分别在路由模块中进行使用
 eg: 'CanActivate'
 const routes: Routes = [
  {
    path: '',
    component: ExampleComponent,
    canActivate: [ExampleGuardService],
    data: {
      title: '测试',
    },
  },
];

eg: 'CanActivateChild'
// 同样把这个 ExampleGuardService
   添加到“无组件的”管理路由(无组件路由:"分组路由,而不需要组件, 一个无组件的路由能让守卫子路由变得更容易"),
   来同时保护它的所有子路由,而不是为每个路由单独添加这个 ExampleGuardService。
 const routes: Routes = [
  {
    path: '',
    component: ExampleComponent,
    canActivateChild: [ExampleGuardService],
    children: [
      {
        path: '',
        component: ExampleChildComponent,
        data: { title: 'test' },
        pathMatch: 'full'
      }]
  },
];

CanDeactivate

应用场景: 处理未保存的更改,相当于在离开这个路由的时候触发组件中的相关判断逻辑

// ExampleDetailDeactivateGuard文件
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { ExampleComponent } from '../exampl/exampl.component';

@Injectable()
export class ExampleDetailDeactivateGuard implements CanDeactivate<ExampleComponent> {
  public canDeactivate(
    component: ExampleComponent,
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | boolean {
    return component.canDeactivate(); // 离开当前路由时,相关的判断是否需要保存逻辑放到具体的组件中
  }
}

// ExampleComponent文件
创建了一个 Guard,它将检查这个(任意)组件中是否有 "canDeactivate()" 函数。 ExampleComponent文件就会有这个方法。
但是该守卫并不需要知道ExampleComponent文件确认退出激活状态的详情。 它只需要检查该组件是否有一个 canDeactivate() 方法,并调用它。
这就让该守卫可以复用。
  public canDeactivate(): Observable<boolean> | boolean {
    // 此组件判断是否离开保存的相关逻辑
  }

希望这篇关于Guard的文章可以对各位小哥哥,小姐姐带来些许的帮助哈 😄(^▽^)~~