嵌套路由
实际应用中,页面通常具有多层级的布局结构。例如,管理后台包含顶部导航和侧边栏,而侧边栏内的不同菜单项对应不同的内容区域。嵌套路由正是用来描述这种父子层级关系的——父路由提供布局外壳,子路由填充具体内容。
基本用法
通过在路由配置中使用 children 字段定义子路由:
ts
const routes = defineRoutes({
path: '/admin',
component: AdminLayout,
children: [
{ path: '', component: AdminDashboard },
{ path: 'users', component: AdminUsers },
{ path: 'settings', component: AdminSettings }
]
})路径拼接规则
子路由的路径是相对于父路由的,最终匹配的完整路径为父路径 + 子路径:
| 子路由 path | 完整匹配路径 |
|---|---|
'' | /admin |
'users' | /admin/users |
'settings' | /admin/settings |
INFO
子路径不需要以 / 开头。即使子路径以 / 开头,也会与父路径拼接后再做路径规范化,不会被视为根路径。如果需要子路由映射到根路径,可以使用别名功能,别名以 / 开头时会被视为根路径。
RouterView 渲染子路由
父路由组件中需要使用 RouterView 来渲染匹配到的子路由组件:
tsx
function AdminLayout() {
return (
<div class="admin-layout">
<aside class="sidebar">
<nav>
<RouterLink to="/admin">仪表盘</RouterLink>
<RouterLink to="/admin/users">用户管理</RouterLink>
<RouterLink to="/admin/settings">系统设置</RouterLink>
</nav>
</aside>
<main class="content">
{/* 子路由组件将在此处渲染 */}
<RouterView />
</main>
</div>
)
}当访问 /admin/users 时,AdminLayout 中的 RouterView 会渲染 AdminUsers 组件。
嵌套层级
嵌套路由支持任意深度的层级嵌套。以下是一个三级嵌套的示例:
ts
const routes = defineRoutes({
path: '/admin',
component: AdminLayout,
children: [
{ path: '', component: AdminDashboard },
{
path: 'users',
component: UserLayout,
children: [
{ path: '', component: UserList },
{ path: '{id}', component: UserDetail }
]
}
]
})对应的路径匹配:
| 完整路径 | 渲染组件链 |
|---|---|
/admin | AdminLayout → AdminDashboard |
/admin/users | AdminLayout → UserLayout → UserList |
/admin/users/123 | AdminLayout → UserLayout → UserDetail |
每一层级的父组件都需要包含 RouterView,以渲染下一层级的子组件。
注意事项
WARNING
包含 children 的路由必须提供 component 或 redirect,否则会抛出错误。这是因为子路由需要一个 RouterView 的挂载点,而 RouterView 只能存在于组件中。
ts
// ❌ 错误:有 children 但没有 component 或 redirect
{
path: '/admin',
children: [...]
}
// ✅ 正确:提供 component 作为布局
{
path: '/admin',
component: AdminLayout,
children: [...]
}
// ✅ 正确:使用 redirect 重定向到子路由
{
path: '/admin',
redirect: '/admin/dashboard',
children: [
{ path: 'dashboard', component: AdminDashboard }
]
}元数据合并
当子路由和父路由都定义了 meta 字段时,子路由的 meta 会与父路由的 meta 进行浅合并,子路由的同名属性会覆盖父路由。合并规则的详细说明请参阅 路由元数据 - 元数据合并。
完整示例
tsx
import { createRouter, defineRoutes, RouterView, RouterLink, useRoute } from 'vitarx-router'
import { createApp } from 'vitarx'
function AdminLayout() {
return (
<div class="admin">
<aside>
<RouterLink to="/admin">仪表盘</RouterLink>
<RouterLink to="/admin/users">用户</RouterLink>
</aside>
<main>
<RouterView />
</main>
</div>
)
}
function AdminDashboard() {
return <h2>管理后台仪表盘</h2>
}
function UserLayout() {
return (
<div>
<h2>用户管理</h2>
<RouterView />
</div>
)
}
function UserList() {
return <p>用户列表</p>
}
function UserDetail() {
const route = useRoute()
return <p>用户详情:{route.params.id}</p>
}
const routes = defineRoutes({
path: '/admin',
component: AdminLayout,
children: [
{ path: '', component: AdminDashboard },
{
path: 'users',
component: UserLayout,
children: [
{ path: '', component: UserList },
{ path: '{id}', component: UserDetail }
]
}
]
})
const router = createRouter({ routes })
function App() {
return <RouterView />
}
createApp(App).use(router).mount('#app')