在 Angular 应用开发中,路由是构建单页应用(SPA)的核心能力之一。基础路由只能满足简单的页面跳转,而实际项目中,我们常遇到 “页面嵌套页面” 的场景 —— 比如侧边栏导航对应主内容区切换,主内容区又包含子标签页。这时候,嵌套路由(父子路由)结合多级路由出口就成为了必掌握的核心技能。本文将从实战角度,手把手教你配置父子路由、使用多级路由出口,解决复杂页面的路由嵌套问题。
一、核心概念先理清
在开始实战前,先明确两个关键概念,避免后续操作混淆:
- 父子路由:路由之间存在层级关系,父路由对应父组件,子路由对应父组件内部的子组件,子路由的路径会拼接在父路由路径之后。
- 路由出口(router-outlet):Angular 渲染路由组件的 “占位符”,默认
<router-outlet></router-outlet>对应根路由出口,也可以通过name属性定义命名路由出口,实现多级 / 多区域渲染。 - 默认子路由:父路由激活时默认显示的子路由,通过
path: ''配置。
二、实战场景:构建带嵌套路由的后台管理页面
我们以 “后台管理系统” 为例,实现以下效果:
- 根路由
/dashboard对应父组件DashboardComponent(包含侧边栏 + 主内容区); - 主内容区为默认路由出口,渲染
/dashboard的子路由:/dashboard/home:首页组件HomeComponent(默认子路由);/dashboard/user:用户管理组件UserComponent;/dashboard/user/:id:用户详情组件UserDetailComponent(User 的子路由);
- 侧边栏右侧新增 “快捷操作” 区域,通过命名路由出口渲染
/dashboard/shortcut对应的ShortcutComponent。
步骤 1:创建所需组件
首先通过 Angular CLI 创建核心组件,确保项目环境已初始化(ng new angular-nested-routes):
# 创建父组件 ng generate component dashboard # 创建dashboard的子组件 ng generate component dashboard/home ng generate component dashboard/user ng generate component dashboard/user-detail # 命名路由出口对应的组件 ng generate component dashboard/shortcut步骤 2:配置嵌套路由(app-routing.module.ts)
嵌套路由的核心是在父路由的children数组中配置子路由,同时通过outlet指定路由出口名称。
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { DashboardComponent } from './dashboard/dashboard.component'; import { HomeComponent } from './dashboard/home/home.component'; import { UserComponent } from './dashboard/user/user.component'; import { UserDetailComponent } from './dashboard/user-detail/user-detail.component'; import { ShortcutComponent } from './dashboard/shortcut/shortcut.component'; // 定义路由规则 const routes: Routes = [ // 根路由重定向到dashboard { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, // 父路由:dashboard { path: 'dashboard', component: DashboardComponent, // 配置子路由 children: [ // 默认子路由:访问/dashboard时,主出口渲染HomeComponent { path: '', component: HomeComponent }, // 子路由1:/dashboard/user,主出口渲染UserComponent { path: 'user', component: UserComponent }, // 子路由2:/dashboard/user/123,主出口渲染UserDetailComponent(带参数) { path: 'user/:id', component: UserDetailComponent }, // 命名路由出口:/dashboard/shortcut,渲染到name="shortcut"的出口 { path: 'shortcut', component: ShortcutComponent, outlet: 'shortcut' // 指定路由出口名称 } ] }, // 404路由 { path: '**', redirectTo: 'dashboard' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], // 根路由配置 exports: [RouterModule] }) export class AppRoutingModule { }步骤 3:配置多级路由出口(组件模板)
1. 根组件模板(app.component.html)
仅保留根路由出口,用于渲染DashboardComponent:
<!-- 根路由出口:渲染DashboardComponent --> <router-outlet></router-outlet>2. 父组件模板(dashboard.component.html)
包含默认路由出口(主内容区)和命名路由出口(快捷操作区),同时添加导航链接:
<div class="dashboard-container" style="display: flex; gap: 20px; padding: 20px;"> <!-- 侧边导航栏 --> <div class="sidebar" style="width: 200px; border-right: 1px solid #eee;"> <h3>后台导航</h3> <!-- 跳转到默认子路由:/dashboard --> <a routerLink="/dashboard" style="display: block; margin: 10px 0;">首页</a> <!-- 跳转到/dashboard/user --> <a routerLink="/dashboard/user" style="display: block; margin: 10px 0;">用户管理</a> <!-- 跳转到带参数的子路由:/dashboard/user/1 --> <a [routerLink]="['/dashboard/user', 1]" style="display: block; margin: 10px 0;">查看用户1详情</a> <!-- 跳转到命名路由出口:shortcut --> <a [routerLink]="[{ outlets: { shortcut: ['dashboard/shortcut'] } }]" style="display: block; margin: 10px 0;">打开快捷操作</a> </div> <!-- 主内容区:默认路由出口(渲染home/user/user-detail) --> <div class="main-content" style="flex: 1;"> <h2>主内容区</h2> <router-outlet></router-outlet> </div> <!-- 快捷操作区:命名路由出口(name="shortcut") --> <div class="shortcut-area" style="width: 200px; border-left: 1px solid #eee; padding: 10px;"> <h3>快捷操作</h3> <router-outlet name="shortcut"></router-outlet> </div> </div>步骤 4:子组件示例(以 UserDetailComponent 为例)
接收路由参数,展示用户 ID:
// user-detail.component.ts import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-user-detail', templateUrl: './user-detail.component.html', styleUrls: ['./user-detail.component.css'] }) export class UserDetailComponent implements OnInit { userId: string = ''; // 注入ActivatedRoute获取路由参数 constructor(private route: ActivatedRoute) { } ngOnInit(): void { // 读取静态参数(适合参数不变化的场景) this.userId = this.route.snapshot.paramMap.get('id') || ''; // 若参数需要动态更新,改用订阅方式: // this.route.paramMap.subscribe(params => { // this.userId = params.get('id') || ''; // }); } }<!-- user-detail.component.html --> <div> <h4>用户详情</h4> <p>用户ID:{{ userId }}</p> </div>步骤 5:运行验证
启动项目ng serve --open,访问http://localhost:4200,验证以下场景:
- 初始页面显示 “首页” 内容,快捷操作区为空;
- 点击 “用户管理”,主内容区切换为用户管理组件;
- 点击 “查看用户 1 详情”,主内容区显示用户 ID=1 的详情;
- 点击 “打开快捷操作”,右侧快捷操作区渲染 ShortcutComponent。
三、常见问题与解决方案
1. 子路由页面空白?
- 检查父组件是否包含
<router-outlet></router-outlet>(子路由必须有出口渲染); - 确认子路由路径配置正确,避免路径拼接错误(如父路由
dashboard+ 子路由/user会变成/user,需去掉子路由路径前的/); - 检查默认子路由是否配置
path: ''且pathMatch: 'full'(根路由)。
2. 命名路由出口跳转失败?
- 命名路由跳转必须使用对象格式:
[routerLink]="[{ outlets: { 出口名: [路由路径] } }]"; - 确保路由配置中
outlet属性值与模板中name属性完全一致(大小写敏感); - 关闭命名路由出口:
[routerLink]="[{ outlets: { shortcut: null } }]"。
3. 路由参数获取不到?
- 确保参数在路由配置中定义(如
:id); - 区分
paramMap(路径参数)和queryParamMap(查询参数,如?id=1); - 动态更新的参数需用订阅方式,而非快照(snapshot)。
四、最佳实践
- 路由模块化:复杂项目建议按模块拆分路由(如
DashboardRoutingModule),通过RouterModule.forChild()配置子模块路由,降低维护成本; - 路由守卫:在嵌套路由中使用
CanActivate/CanDeactivate守卫,控制父 / 子路由的访问权限; - 懒加载模块:对大型模块配置懒加载,减少首屏加载体积:
// 懒加载示例 { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) } - 路由复用:通过
RouteReuseStrategy缓存子路由组件,避免重复渲染,提升体验。
五、总结
Angular 嵌套路由的核心是父子路由配置和多级路由出口的配合:
- 父子路由通过
children数组实现层级关系,子路由路径会拼接在父路由之后,父组件需包含<router-outlet>渲染子组件; - 命名路由出口通过
outlet属性和name属性绑定,实现多区域独立渲染; - 路由参数通过
ActivatedRoute获取,静态参数用快照,动态参数用订阅。
掌握嵌套路由后,你可以轻松构建复杂的层级化页面结构,满足后台管理系统、电商详情页等绝大多数 SPA 的路由需求。建议结合 Angular 官方文档,尝试在项目中封装路由工具类,进一步提升路由管理的效率。