位置: 文档库 > JavaScript > Angular路由内路由守卫该如何使用

Angular路由内路由守卫该如何使用

TitanPetal 上传于 2022-03-30 11:14

《Angular路由内路由守卫该如何使用》

在Angular应用开发中,路由守卫(Route Guards)是控制导航行为的核心机制。它允许开发者在路由激活前、激活后或路由数据加载时执行特定逻辑,常用于权限验证、数据预加载或取消导航等场景。本文将深入探讨Angular路由守卫的分类、实现方式及实际应用案例,帮助开发者高效管理路由访问权限。

一、路由守卫的核心类型

Angular提供了五种内置的路由守卫接口,每种守卫对应不同的导航阶段:

1. CanActivate(可激活守卫)

用于判断用户是否有权限访问目标路由。若返回`false`或`Observable`/`Promise`为`false`,则导航被取消。

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private router: Router) {}

  canActivate() {
    const isLoggedIn = localStorage.getItem('token');
    if (!isLoggedIn) {
      this.router.navigate(['/login']);
      return false;
    }
    return true;
  }
}

2. CanActivateChild(子路由可激活守卫)

与`CanActivate`类似,但作用于子路由模块。适用于需要统一验证子路由权限的场景。

@Injectable()
export class AdminChildGuard implements CanActivateChild {
  canActivateChild() {
    return checkAdminPermission(); // 自定义权限验证逻辑
  }
}

3. CanDeactivate(可取消激活守卫)

用于在离开当前路由前确认是否允许导航。常用于表单未保存时的提示。

import { CanDeactivate } from '@angular/router';
import { EditComponent } from './edit.component';

export interface CanComponentDeactivate {
  canDeactivate: () => boolean | Observable | Promise;
}

@Injectable()
export class UnsavedGuard implements CanDeactivate {
  canDeactivate(component: CanComponentDeactivate) {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

4. Resolve(数据预加载守卫)

在路由激活前预加载数据,确保组件初始化时数据已就绪。

import { Resolve } from '@angular/router';
import { DataService } from './data.service';

@Injectable()
export class UserResolver implements Resolve {
  constructor(private dataService: DataService) {}

  resolve() {
    return this.dataService.getUser();
  }
}

5. CanLoad(可加载守卫)

控制是否允许加载异步路由模块(Lazy Loading)。适用于模块级权限验证。

@Injectable()
export class ModuleLoadGuard implements CanLoad {
  canLoad() {
    return checkFeatureFlag(); // 检查功能开关
  }
}

二、路由守卫的实现步骤

1. 创建守卫服务

通过Angular CLI生成守卫服务:

ng generate guard auth/auth --implements CanActivate

或手动创建类并实现对应接口。

2. 注册守卫到路由配置

在路由模块中配置守卫:

const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [AuthGuard],
    canActivateChild: [AdminChildGuard],
    resolve: { user: UserResolver }
  },
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
    canLoad: [ModuleLoadGuard]
  }
];

3. 组合使用多个守卫

多个守卫按声明顺序依次执行,若任一守卫返回`false`,导航终止:

canActivate: [AuthGuard, RoleGuard, PermissionGuard]

三、实际应用场景

场景1:基于角色的权限控制

结合用户角色动态验证路由访问:

@Injectable()
export class RoleGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot) {
    const requiredRole = route.data['role'];
    const userRole = this.authService.getUserRole();
    
    if (userRole !== requiredRole) {
      this.router.navigate(['/forbidden']);
      return false;
    }
    return true;
  }
}

路由配置中通过`data`属性传递角色要求:

{
  path: 'admin',
  component: AdminComponent,
  canActivate: [RoleGuard],
  data: { role: 'admin' }
}

场景2:表单数据保护

防止用户意外离开未保存的表单:

@Component({
  template: `
    
` }) export class EditComponent implements CanComponentDeactivate { @ViewChild('form') form: NgForm; hasChanges = false; canDeactivate() { if (this.hasChanges && !confirm('确定要放弃更改吗?')) { return false; } return true; } }

场景3:异步数据预加载

使用`Resolve`守卫优化组件加载体验:

// 守卫实现
@Injectable()
export class ProductResolver implements Resolve {
  constructor(private productService: ProductService) {}

  resolve() {
    return this.productService.getProducts().pipe(
      catchError(error => {
        console.error('数据加载失败', error);
        return of(null);
      })
    );
  }
}

// 组件中获取数据
export class ProductListComponent implements OnInit {
  products: Product[];

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.products = this.route.snapshot.data['products'];
  }
}

四、高级技巧与注意事项

1. 守卫中的异步处理

守卫支持返回`Observable`或`Promise`,适用于需要等待异步操作完成的场景:

canActivate(): Observable {
  return this.authService.checkPermission().pipe(
    map(hasPermission => hasPermission),
    catchError(() => of(false))
  );
}

2. 全局守卫与路由守卫的区别

全局守卫(如`APP_INITIALIZER`)在应用启动时执行,而路由守卫针对特定路由。两者可结合使用实现更复杂的逻辑。

3. 守卫的测试策略

使用Angular Testing模块模拟依赖:

describe('AuthGuard', () => {
  let guard: AuthGuard;
  let mockRouter = { navigate: jasmine.createSpy('navigate') };

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        AuthGuard,
        { provide: Router, useValue: mockRouter }
      ]
    });
    guard = TestBed.inject(AuthGuard);
  });

  it('should redirect unauthorized users', () => {
    expect(guard.canActivate()).toBe(false);
    expect(mockRouter.navigate).toHaveBeenCalledWith(['/login']);
  });
});

4. 性能优化

避免在守卫中执行耗时操作,必要时使用缓存或状态管理(如NgRx)存储验证结果。

五、常见问题解答

Q1:守卫执行顺序如何控制?

路由配置中守卫按声明顺序执行,可通过数组顺序或自定义优先级逻辑控制。

Q2:如何实现跨模块守卫?

将守卫服务提供到根模块(`providedIn: 'root'`)或共享模块中,确保所有路由模块均可注入。

Q3:守卫能否访问路由参数?

可以通过`ActivatedRouteSnapshot`获取当前路由参数:

canActivate(route: ActivatedRouteSnapshot) {
  const id = route.params['id'];
  // 根据id执行验证
}

六、总结

Angular路由守卫是构建安全、高效应用的关键工具。通过合理使用`CanActivate`、`CanDeactivate`、`Resolve`等守卫,开发者可以实现细粒度的权限控制、数据预加载和导航保护。结合实际应用场景,掌握守卫的组合使用和异步处理技巧,能够显著提升应用的用户体验和安全性。

关键词:Angular路由守卫、CanActivate、权限控制、数据预加载、CanDeactivate、路由模块、Lazy Loading异步验证

简介:本文详细介绍了Angular路由守卫的五种类型(CanActivate、CanActivateChild、CanDeactivate、Resolve、CanLoad)的实现方式与应用场景,通过代码示例展示了权限验证、表单保护、数据预加载等实际用法,并提供了测试策略和性能优化建议,帮助开发者高效管理路由访问权限。