Angular中如何将参数化路由设为默认路由并避免组件重复初始化
计划将应用首页设置为/lists/{listId}/snippets/{snippetId}路由,当前根路由文件app-routing.module.ts配置如下:
const routes: Routes = [ { path: 'lists', loadChildren: () => import('./pages/home/home.module').then((m) => m.HomeModule), canActivate: [AuthGuard], // NOTE: don't use patchMatch: 'full' for lazy-loaded modules pathMatch: 'prefix', }, { path: 'login', loadChildren: () => import('./pages/auth/auth.module').then((m) => m.AuthModule), canActivate: [GuessGuard], }, { path: '', redirectTo: '/lists', pathMatch: 'full', }, { path: '**', redirectTo: '/', pathMatch: 'full', }, ];
懒加载的Home模块内路由配置如下(当前存在语法错误,多嵌套了一层大括号):
const homeRoutes: Routes = [ { path: '', component: HomeComponent, }, { { path: ':listId/snippets/:snippetId', component: HomeComponent, }, } ]
如果在HomeComponent的初始化逻辑中调用this.router.navigate(['/lists', listId, 'snippets', 'snippetId'])跳转到目标首页路由,会触发HomeComponent重复初始化,需要更优的实现方案。
注:你描述的目标路径为/list/{listId}/snippets/{snippetId},但现有路由配置前缀为lists,注意保持路径统一避免触发通配符路由跳转。
方案1:调整懒加载模块路由配置,用重定向替代重复挂载组件
这是改造成本最低的方案,核心思路是不要给两个不同的路由路径绑定同一个组件,避免Angular判定为路由切换触发组件销毁重建。
移除空路径下对HomeComponent的挂载,直接将空路径重定向到带默认参数的目标路由,全程只在带参路由下挂载一次HomeComponent,从根源上避免跳转导致的组件重建。
修正后的Home模块路由配置参考:
const homeRoutes: Routes = [ { path: ':listId/snippets/:snippetId', component: HomeComponent, }, { path: '', // 替换为业务中默认加载的首个listId、snippetId redirectTo: '1/snippets/1', pathMatch: 'full' } ]
如果默认的listId、snippetId需要从本地缓存/用户配置中读取,可以在重定向时用相对路径拼接,不需要经过组件实例。
方案2:将跳转逻辑前置到路由守卫中执行
如果默认参数需要异步获取(比如请求接口拉取用户最近访问的片段),不要把跳转逻辑写在HomeComponent的ngOnInit中,把逻辑移到/lists路由的canActivate守卫里:
- 在守卫中完成默认参数的获取
- 直接通过
Router.parseUrl()返回目标路由的UrlTree
这种方式会在路由激活阶段就完成跳转,不会先初始化空路径对应的HomeComponent,自然不会出现重复初始化的问题。
守卫逻辑参考:
@Injectable({ providedIn: 'root' }) export class HomeRedirectGuard implements CanActivate { constructor(private router: Router, private userService: UserService) {} canActivate(): Observable<boolean | UrlTree> { return this.userService.getLastVisitSnippet().pipe( map(({ listId, snippetId }) => this.router.parseUrl(`/lists/${listId}/snippets/${snippetId}`) ) ) } }
之后把该守卫添加到根路由/lists的canActivate数组中即可。
额外注意事项
当路由已经处于/lists/:listId/snippets/:snippetId路径下,后续切换listId、snippetId时,Angular默认会复用当前HomeComponent实例,不会触发组件重建。只需要在组件中订阅ActivatedRoute.paramMap获取参数变化,更新视图数据即可,不需要做额外的路由复用配置。
内容的提问来源于stack exchange,提问作者The.Wolfgang.Grimmer

