62. 路由配置与嵌套路由

📖 概述

嵌套路由是Vue Router的核心功能之一,允许我们在一个路由组件内部嵌套另一个路由组件,从而实现更复杂的页面结构。本集将深入讲解Vue Router 4.x的路由配置进阶技巧,包括嵌套路由的定义、配置、使用场景以及最佳实践,帮助你构建具有多层次结构的单页应用。

✨ 核心知识点

1. 嵌套路由基础

什么是嵌套路由

  • 嵌套路由允许在一个路由组件内部渲染另一个路由组件
  • 形成父子路由关系,反映页面的层次结构
  • 适用于具有复杂布局的应用,如后台管理系统

嵌套路由的使用场景

  • 后台管理系统:主布局包含侧边栏和主内容区,主内容区根据路由动态变化
  • 电商网站:产品详情页包含多个标签页(介绍、评论、规格等)
  • 社交媒体应用:用户主页包含动态、相册、好友列表等

2. 配置嵌套路由

基本配置

// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/HomeView.vue')
  },
  {
    path: '/dashboard',
    name: 'dashboard',
    component: () => import('../views/DashboardView.vue'),
    // 嵌套路由配置
    children: [
      {
        // 子路由路径,相对于父路由
        path: '', // 空路径表示默认子路由
        name: 'dashboard-home',
        component: () => import('../views/DashboardHomeView.vue')
      },
      {
        path: 'profile',
        name: 'dashboard-profile',
        component: () => import('../views/DashboardProfileView.vue')
      },
      {
        path: 'settings',
        name: 'dashboard-settings',
        component: () => import('../views/DashboardSettingsView.vue')
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

export default router

父组件中的路由视图

<!-- src/views/DashboardView.vue -->
<template>
  <div class="dashboard">
    <!-- 侧边栏导航 -->
    <aside class="sidebar">
      <h2>仪表盘</h2>
      <nav>
        <router-link to="/dashboard">首页</router-link>
        <router-link to="/dashboard/profile">个人资料</router-link>
        <router-link to="/dashboard/settings">设置</router-link>
      </nav>
    </aside>
    
    <!-- 主内容区,渲染子路由组件 -->
    <main class="content">
      <router-view />
    </main>
  </div>
</template>

<script setup lang="ts">
// Dashboard组件逻辑
</script>

<style scoped>
.dashboard {
  display: flex;
  height: 100vh;
}

.sidebar {
  width: 200px;
  background-color: #2c3e50;
  color: white;
  padding: 20px;
}

.content {
  flex: 1;
  padding: 20px;
  background-color: #f5f5f5;
}

nav a {
  display: block;
  color: white;
  text-decoration: none;
  padding: 10px 0;
  margin: 10px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

nav a.router-link-exact-active {
  color: #42b983;
  font-weight: bold;
}
</style>

子路由组件

<!-- src/views/DashboardHomeView.vue -->
<template>
  <div>
    <h1>仪表盘首页</h1>
    <p>欢迎来到仪表盘!</p>
  </div>
</template>

<script setup lang="ts">
// DashboardHome组件逻辑
</script>
<!-- src/views/DashboardProfileView.vue -->
<template>
  <div>
    <h1>个人资料</h1>
    <p>这里是您的个人资料页面。</p>
  </div>
</template>

<script setup lang="ts">
// DashboardProfile组件逻辑
</script>
<!-- src/views/DashboardSettingsView.vue -->
<template>
  <div>
    <h1>设置</h1>
    <p>这里是设置页面。</p>
  </div>
</template>

<script setup lang="ts">
// DashboardSettings组件逻辑
</script>

3. 嵌套路由的高级配置

多级嵌套路由

// src/router/index.ts
const routes: Array<RouteRecordRaw> = [
  {
    path: '/admin',
    name: 'admin',
    component: () => import('../views/AdminLayout.vue'),
    children: [
      {
        path: '',
        name: 'admin-home',
        component: () => import('../views/AdminHomeView.vue')
      },
      {
        path: 'users',
        name: 'admin-users',
        component: () => import('../views/AdminUsersLayout.vue'),
        // 二级嵌套路由
        children: [
          {
            path: '',
            name: 'admin-users-list',
            component: () => import('../views/AdminUsersListView.vue')
          },
          {
            path: ':id',
            name: 'admin-user-detail',
            component: () => import('../views/AdminUserDetailView.vue')
          },
          {
            path: ':id/edit',
            name: 'admin-user-edit',
            component: () => import('../views/AdminUserEditView.vue')
          }
        ]
      }
    ]
  }
]

动态嵌套路由

// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

// 动态生成路由
function generateRoutes(): Array<RouteRecordRaw> {
  const dynamicRoutes: Array<RouteRecordRaw> = [
    {
      path: '/dynamic',
      name: 'dynamic',
      component: () => import('../views/DynamicLayout.vue'),
      children: [
        {
          path: 'page1',
          name: 'dynamic-page1',
          component: () => import('../views/DynamicPage1.vue')
        },
        {
          path: 'page2',
          name: 'dynamic-page2',
          component: () => import('../views/DynamicPage2.vue')
        }
      ]
    }
  ]
  
  // 根据权限动态添加路由
  const hasPermission = true
  if (hasPermission) {
    dynamicRoutes[0].children?.push({
      path: 'page3',
      name: 'dynamic-page3',
      component: () => import('../views/DynamicPage3.vue')
    })
  }
  
  return dynamicRoutes
}

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/HomeView.vue')
  },
  // 合并动态生成的路由
  ...generateRoutes()
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

4. 路由配置的高级技巧

1. 路由别名

const routes: Array<RouteRecordRaw> = [
  {
    path: '/home',
    name: 'home',
    component: HomeView,
    // 路由别名,可以通过多个URL访问同一组件
    alias: ['/', '/welcome']
  }
]

2. 路由重定向

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    // 重定向到home路由
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'home',
    component: HomeView
  },
  {
    path: '/old-path',
    // 重定向到新路由
    redirect: { name: 'new-path' }
  },
  {
    path: '/new-path',
    name: 'new-path',
    component: NewPathView
  },
  {
    path: '/user/:id',
    // 动态重定向
    redirect: to => {
      // to是路由对象,可以获取参数、查询等
      return { path: `/profile/${to.params.id}` }
    }
  }
]

3. 路由组件传参

const routes: Array<RouteRecordRaw> = [
  {
    path: '/user/:id',
    name: 'user',
    component: UserView,
    // 路由组件传参,将路由参数作为props传递给组件
    props: true
  },
  {
    path: '/search',
    name: 'search',
    component: SearchView,
    // 将查询参数作为props传递给组件
    props: route => ({ query: route.query.q })
  },
  {
    path: '/static',
    name: 'static',
    component: StaticView,
    // 静态props
    props: { staticProp: 'value' }
  }
]

4. 路由懒加载的分组

const routes: Array<RouteRecordRaw> = [
  {
    path: '/home',
    name: 'home',
    component: () => import('../views/HomeView.vue')
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue')
  },
  // 将多个路由组件打包到同一个chunk中
  {
    path: '/admin',
    name: 'admin',
    component: () => import(/* webpackChunkName: "admin" */ '../views/AdminView.vue')
  },
  {
    path: '/admin/users',
    name: 'admin-users',
    component: () => import(/* webpackChunkName: "admin" */ '../views/AdminUsersView.vue')
  }
]

5. 组合式API中的路由使用

1. useRouter和useRoute

<script setup lang="ts">
import { useRouter, useRoute } from 'vue-router'

// 获取路由实例
const router = useRouter()
// 获取当前路由信息
const route = useRoute()

// 获取路由参数
const userId = route.params.id
// 获取查询参数
const query = route.query
// 获取路由元信息
const meta = route.meta

// 编程式导航
function navigateToHome() {
  router.push('/home')
}

function navigateToUser(id: string) {
  router.push({ name: 'user', params: { id } })
}
</script>

2. 监听路由变化

<script setup lang="ts">
import { useRoute, watch } from 'vue-router'

const route = useRoute()

// 监听路由变化
watch(
  () => route.params,
  (newParams, oldParams) => {
    // 处理路由参数变化
    console.log('路由参数变化:', newParams, oldParams)
  },
  { deep: true }
)

// 监听整个路由对象变化
watch(
  () => route,
  (newRoute, oldRoute) => {
    // 处理路由变化
    console.log('路由变化:', newRoute, oldRoute)
  },
  { deep: true }
)
</script>

📝 最佳实践

  1. 合理设计路由结构

    • 路由结构应反映页面的层次结构
    • 避免过深的嵌套路由(建议不超过3层)
    • 使用模块化管理复杂路由
  2. 使用命名路由

    • 优先使用命名路由进行导航
    • 提高代码的可读性和可维护性
    • 便于路由重构
  3. 实现路由懒加载

    • 对所有路由组件使用懒加载
    • 合理分组路由组件,优化加载性能
    • 使用webpackChunkName注释进行代码分割
  4. 配置默认子路由

    • 为父路由配置默认子路由
    • 避免出现空白内容区域
    • 提高用户体验
  5. 使用路由组件传参

    • 优先使用props将路由参数传递给组件
    • 使组件更独立,便于测试和复用
  6. 实现路由重定向

    • 为旧路由配置重定向,确保链接可用性
    • 使用动态重定向处理复杂场景
  7. 添加路由元信息

    • 使用meta字段存储路由的附加信息
    • 用于权限控制、菜单生成、页面标题等
  8. 使用组合式API

    • 在Vue 3中优先使用useRouter和useRoute
    • 与Composition API无缝配合
    • 提高代码的可组合性

💡 常见问题与解决方案

  1. 嵌套路由不显示

    • 检查父组件是否包含&lt;router-view&gt;
    • 检查子路由路径是否正确
    • 确保路由配置顺序正确
  2. 默认子路由不生效

    • 检查默认子路由的path是否为空
    • 确保默认子路由在children数组中
  3. 路由参数不更新

    • 使用watch监听路由参数变化
    • 避免在组件初始化时只获取一次参数
    • 使用组合式API的watch函数
  4. 路由懒加载失败

    • 检查动态import语法是否正确
    • 确保构建工具支持ES模块
    • 检查组件路径是否正确
  5. 路由重定向循环

    • 避免路由重定向到自身或形成循环链
    • 使用命名路由进行重定向
    • 检查重定向逻辑
  6. 路由顺序问题

    • 路由匹配是从上到下的,确保更具体的路由在前面
    • 避免通配符路由(*)在前面

📚 进一步学习资源

🎯 课后练习

  1. 基础练习

    • 创建一个包含嵌套路由的Vue 3应用
    • 实现一个简单的后台管理系统布局
    • 配置默认子路由和多级嵌套路由
  2. 进阶练习

    • 实现路由别名和重定向
    • 使用路由组件传参
    • 配置路由懒加载的分组
  3. 实战练习

    • 构建一个完整的后台管理系统路由结构
    • 实现动态路由生成
    • 使用组合式API处理路由
  4. 性能优化练习

    • 分析路由懒加载的打包结果
    • 优化路由组件的代码分割
    • 测试不同路由配置的加载性能

通过本集的学习,你已经掌握了Vue Router 4.x的路由配置进阶技巧,包括嵌套路由的定义、配置、使用场景以及最佳实践。在实际项目中,合理设计和配置路由,能够构建出具有良好层次结构、性能优良的单页应用。下一集我们将深入学习编程式导航方法大全,进一步提升Vue 3路由系统的使用能力。

« 上一篇 Vue3 + TypeScript 系列教程 - 第61集:Vue Router 4.x安装配置 下一篇 » Vue3 + TypeScript 系列教程 - 第63集:编程式导航方法大全