logo
  • 指南
  • API
  • 博客
  • 简体中文
    • 简体中文
    • English
    • @esmx/core
      Esmx
      App
      RenderContext
      ModuleConfig
      PackConfig
      ManifestJson
      @esmx/router
      Router
      Route
      路由配置
      RouterLink
      导航守卫
      动态路由匹配
      嵌套路由
      编程式导航
      滚动行为
      图层路由
      微应用
      错误类型
      类型参考
      @esmx/router-vue
      RouterPlugin
      组合式函数
      组件
      类型增强
      @esmx/router-react
      微应用集成
      Hooks 与上下文
      组件
      SSR
      App
      @esmx/rspack
      @esmx/rspack-vue
      @esmx/rspack-react

      最后更新于: 2026/4/7 02:16:07

      上一页动态路由匹配下一页编程式导航

      #嵌套路由

      实际应用的 UI 通常由多层嵌套的组件组成。布局包裹页面内容,或者页面拥有自己的子页面,这些都是非常常见的情况。@esmx/router 使用嵌套路由配置和 RouterView 组件来自然地表达这种关系。

      #基本嵌套

      考虑一个带有导航栏和内容区域的布局,不同页面在内容区域中渲染:

      ┌──────────────────────────────────┐
      │  Navigation Bar                  │
      ├──────────────────────────────────┤
      │                                  │
      │  <RouterView />                  │
      │  (renders Home, About, etc.)     │
      │                                  │
      └──────────────────────────────────┘

      这通过路由配置中的 children 来表达:

      import type { RouteConfig } from '@esmx/router';
      
      const routes: RouteConfig[] = [
        {
          path: '/',
          component: Layout,
          children: [
            { path: '', component: Home },         // matches /
            { path: 'about', component: About },   // matches /about
            { path: 'contact', component: Contact } // matches /contact
          ]
        }
      ];

      Layout 组件使用 RouterView 来渲染匹配到的子路由:

      src/Layout.vue
      <template>
        <div class="layout">
          <nav>
            <RouterLink to="/">Home</RouterLink>
            <RouterLink to="/about">About</RouterLink>
            <RouterLink to="/contact">Contact</RouterLink>
          </nav>
      
          <main>
            <RouterView />
          </main>
        </div>
      </template>

      当用户导航到 /about 时,Layout 组件保持挂载,只有 RouterView 的内容从 Home 切换为 About。

      Tip

      注意,空路径('')的子路由充当默认子路由。它在父路由路径完全匹配时匹配——在这个例子中是 /。

      #matched 的工作原理

      当 URL 与嵌套路由匹配时,route.matched 数组包含所有匹配的路由配置,按从父级到子级的顺序排列:

      // 路由配置
      {
        path: '/',          // matched[] 中的索引 0
        component: Layout,
        children: [
          {
            path: 'users/:id',   // matched[] 中的索引 1
            component: UserProfile
          }
        ]
      }
      
      // 当 URL 为 /users/42 时:
      route.matched[0]  // → Layout 路由配置
      route.matched[1]  // → UserProfile 路由配置
      route.matched.length  // 2

      每个 RouterView 组件使用它在组件树中的深度来从 matched[] 中选取正确的条目。根级 RouterView 渲染 matched[0],嵌套的 RouterView 渲染 matched[1],依此类推。

      #RouterView 组件

      RouterView 从 @esmx/router-vue 导入。它渲染路由树中当前深度的组件:

      import { RouterView } from '@esmx/router-vue';

      或通过插件全局注册:

      import { RouterPlugin } from '@esmx/router-vue';
      app.use(RouterPlugin); // 全局注册 RouterView 和 RouterLink

      RouterView 自动跟踪其嵌套深度。你不需要传递任何 props——它根据自身在组件层级中的位置,知道应该渲染哪个匹配的路由配置。

      #多级嵌套

      路由可以嵌套到任意深度。每一级嵌套对应组件树中的一个 RouterView:

      const routes: RouteConfig[] = [
        {
          path: '/',
          component: AppLayout,           // 深度 0
          children: [
            {
              path: 'users',
              component: UsersLayout,      // 深度 1
              children: [
                { path: '', component: UserList },         // 深度 2
                {
                  path: ':id',
                  component: UserDetailLayout,              // 深度 2
                  children: [
                    { path: '', component: UserProfile },   // 深度 3
                    { path: 'posts', component: UserPosts } // 深度 3
                  ]
                }
              ]
            }
          ]
        }
      ];

      /users/42/posts 的组件树如下所示:

      AppLayout                      ← matched[0],由深度 0 的 RouterView 渲染
      └── UsersLayout                ← matched[1],由深度 1 的 RouterView 渲染
          └── UserDetailLayout       ← matched[2],由深度 2 的 RouterView 渲染
              └── UserPosts          ← matched[3],由深度 3 的 RouterView 渲染

      每个布局组件包含一个 RouterView,用于渲染下一级:

      src/UsersLayout.vue
      <template>
        <div class="users-layout">
          <h1>Users</h1>
          <RouterView />
        </div>
      </template>
      src/UserDetailLayout.vue
      <template>
        <div class="user-detail">
          <UserSidebar />
          <RouterView />
        </div>
      </template>

      #RouterView 深度

      每个 RouterView 内部维护一个深度计数器。根级 RouterView 的深度为 0,每个嵌套的 RouterView 将深度加 1。这个深度决定了渲染 route.matched 数组中的哪个条目。

      你可以使用 useRouterViewDepth() 访问当前深度:

      <script setup lang="ts">
      import { useRouterViewDepth } from '@esmx/router-vue';
      
      const depth = useRouterViewDepth();
      console.log('Current RouterView depth:', depth); // 0, 1, 2, etc.
      </script>
      • 深度 0:渲染 route.matched[0].component(根布局)
      • 深度 1:渲染 route.matched[1].component(分区布局)
      • 深度 2:渲染 route.matched[2].component(页面组件)
      • 深度 3:渲染 route.matched[3].component(子页面组件)

      这一切都是自动处理的——你很少需要直接与深度交互。它主要暴露用于高级用例,如构建自定义 RouterView 实现。

      #完整示例

      这是一个带有三级布局结构的完整示例:

      #路由配置

      src/routes.ts
      import type { RouteConfig } from '@esmx/router';
      
      export const routes: RouteConfig[] = [
        {
          path: '/',
          component: MainLayout,
          children: [
            { path: '', component: HomePage },
            { path: 'about', component: AboutPage },
            {
              path: 'users',
              component: UsersSection,
              children: [
                { path: '', component: UserList },
                {
                  path: ':id',
                  component: UserProfile,
                  children: [
                    { path: '', component: UserOverview },
                    { path: 'settings', component: UserSettings }
                  ]
                }
              ]
            }
          ]
        }
      ];

      #布局组件

      src/MainLayout.vue
      <template>
        <div class="app">
          <header>
            <RouterLink to="/">Home</RouterLink>
            <RouterLink to="/about">About</RouterLink>
            <RouterLink to="/users">Users</RouterLink>
          </header>
      
          <RouterView />
      
          <footer>© 2024</footer>
        </div>
      </template>
      src/UsersSection.vue
      <template>
        <div class="users-section">
          <aside>
            <h2>Users</h2>
            <UserNavigation />
          </aside>
      
          <div class="users-content">
            <RouterView />
          </div>
        </div>
      </template>
      src/UserProfile.vue
      <template>
        <div class="user-profile">
          <h2>User {{ route.params.id }}</h2>
          <nav>
            <RouterLink :to="`/users/${route.params.id}`">Overview</RouterLink>
            <RouterLink :to="`/users/${route.params.id}/settings`">Settings</RouterLink>
          </nav>
      
          <RouterView />
        </div>
      </template>
      
      <script setup lang="ts">
      import { useRoute } from '@esmx/router-vue';
      const route = useRoute();
      </script>

      #导航结果

      URL渲染的组件
      /MainLayout → HomePage
      /aboutMainLayout → AboutPage
      /usersMainLayout → UsersSection → UserList
      /users/42MainLayout → UsersSection → UserProfile → UserOverview
      /users/42/settingsMainLayout → UsersSection → UserProfile → UserSettings