订阅路由守卫中的可观察对象及其含义

Subscribing to observables in route guards and its implications

我有一个名为 PermissionGuard 的路由器防护,它是在这里启动的

const routes: Routes = [
  {
    path: ':company',
    component: CompanyComponent,
    canActivate: [PermissionGuard],
    canActivateChild: [PermissionGuard],
    children: [
      {
        path: '',
        component: IndexComponent
      },
      {
        path: 'projects',
        loadChildren: '../projects/projects.module#ProjectsModule'
      },
    ]
  }
];

在我的 PermissionGuard 我订阅了一个 PermissionService 这样的:

export class PermissionGuard implements CanActivate, CanActivateChild {

  private permissions: Permission[];

  constructor(private permissionService: PermissionService, private router: Router) {
    this.permissionService.permissions$.subscribe(
      (permissions: Permission[]) => {
        this.permissions = permissions;
      }
    );
  }

  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    this.permissionService.getPermissions(next.paramMap.get('company'));
    return this.permissionService.permissionsFetchDone$
    .pipe(filter(x => x))
    .pipe(
      map(response => {
        if (this.permissions) {
          return true;
        } else {
          this.router.navigate(['/forbidden']);
        }
      })
    );
  }
}

并根据此数据执行必要的 canActivatecanActivateChild 检查。通过在子路由中添加 console.log() 并从 permissions$ 发出新数据,我可以看到 observable 仍然处于活动状态,即使守卫已经 "used" 并且路由已激活。当我去 path: ':company' 以外的路线时,我期待它会消失,但是守卫并没有被摧毁。

这引出了我的问题: 我这样做正确吗?我希望使用守卫来检查用户是否有任何权限,但同时我只想执行一次 HTTP 权限请求(当导航到 path: ':company' 或其任何子项时)。我担心如果我像这样使用守卫,由于大量的观察者,它会及时减慢整个应用程序。

emitting new data from permissions$ inside the children routes I can see that the observable is still active, even though the guard has been "used" and the route is activated.

首先,permissions$ 仍然有效,因为您从未取消订阅您的守卫。 angular 将守卫创建为单例。

即使守卫不是单例,守卫的实例仍然通过 this.permissions 在订阅中引用,并且如果您的 observable 作为变量存在于您的 auth 服务中(我假设它是一个单例)这个绑定还会阻止垃圾回收。

This brings me to my question: Am I doing this correctly? I wish to use a guard to check if the user has any permissions,

在你的守卫中发出请求是完全可以的,例如获取权限。

but at the same time I want to perform the HTTP request for permissions only once (when navigating to path: ':company' or any of its children).

如果您只想发出一次请求,那么您应该考虑在您的身份验证服务中使用 shareReplay,以便所有未来连接的实例都使用相同的先前发出的值。