我的 Angular 偏好: 给用来中转的父路由单独声明一个路由并用 redirectTo 来中转

94 阅读1分钟

对于如下情况,你会在哪里添加重定向逻辑?

flowchart TD
    Customer --> ChildRoute["/child"]
    ChildRoute --> Condition{Condition?}
    Condition -->|是| GrandChildRoute1["/child/grandchild1"]
    Condition -->|否| GrandChildRoute2["/child/grandchild2"]

实现方式有很多种。我更倾向于在 Angular@18 中使用 redirectTo 函数,在 child 路由下添加一个专门用于重定向的路由:

export const routes: Routes = [
  {
    path: "child",
    component: ChildComponent,
    children: [
      {
        path: "",
        pathMatch: "full",
        redirectTo: () => {
          const shouldRedirectToChild1 = Math.random() > 0.5;
          if (shouldRedirectToChild1) {
            return "grandchild1";
          }
          return "grandchild2";
        },
      },
      {
        path: "grandchild1",
        loadComponent: () => GrandChild1Component,
      },
      {
        path: "grandchild2",
        loadComponent: () => GrandChild2Component,
      },
    ],
  },
];

在 Angular@18 之前,我会把该逻辑放在 canActivate 中,像这样:

export const routes: Routes = [
  {
    path: "child",
    component: ChildComponent,
    children: [
      {
        path: "",
        pathMatch: "full",
        component: ChildComponent,
        canActivate: [
          () => {
            const router = inject(Router);
            const shouldRedirectToChild1 = Math.random() > 0.5;
            if (shouldRedirectToChild1) {
              return router.parseUrl("/child/grandchild1");
            }
            return router.parseUrl("/child/grandchild2");
          },
        ],
      },
      {
        path: "grandchild1",
        loadComponent: () => GrandChild1Component,
      },
      {
        path: "grandchild2",
        loadComponent: () => GrandChild2Component,
      },
    ],
  },
];

从我的角度来看,这种方式不那么好,原因如下:

  • canActivate 不如 redirectTo 直观;
  • 在这种情况下,canActivate 需要 component 字段才能工作且不报错,但实际上并不需要组件。

还有一种解决方案如下:

export const routes: Routes = [
  {
    path: "child",
    component: ChildComponent,
    canActivate: [
      (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
        const isChildRoute = state.url === "/child";
        if (isChildRoute) {
          const shouldRedirectToChild1 = Math.random() > 0.5;
          if (shouldRedirectToChild1) {
            return "grandchild1";
          }
          return "grandchild2";
        }
        return true;
      },
    ],
    children: [
      {
        path: "grandchild1",
        loadComponent: () => GrandChild1Component,
      },
      {
        path: "grandchild2",
        loadComponent: () => GrandChild2Component,
      },
    ],
  },
];

这种方式的问题很明显:

  • 我们需要判断用户是否直接访问了父级路径,例如:isChildRoute = state.url === "/child"
  • 很容易在这个 canActivate 函数中加入其他非重定向相关的逻辑,违反单一职责原则,例如权限相关代码;
  • 当路由从 /child/grandchild2 切换到 /child 时,由于 /child 路由已经被激活,上述 canActivate 将不会被触发。

那么你怎么看?