嵌套路由

实际应用中,页面通常具有多层级的布局结构。例如,管理后台包含顶部导航和侧边栏,而侧边栏内的不同菜单项对应不同的内容区域。嵌套路由正是用来描述这种父子层级关系的——父路由提供布局外壳,子路由填充具体内容。

基本用法

通过在路由配置中使用 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 }
      ]
    }
  ]
})

对应的路径匹配:

完整路径渲染组件链
/adminAdminLayoutAdminDashboard
/admin/usersAdminLayoutUserLayoutUserList
/admin/users/123AdminLayoutUserLayoutUserDetail

每一层级的父组件都需要包含 RouterView,以渲染下一层级的子组件。

注意事项

WARNING

包含 children 的路由必须提供 componentredirect,否则会抛出错误。这是因为子路由需要一个 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')