Vue.js路由配置

路由配置是 Vue Router 的核心,它定义了 URL 路径与 Vue 组件之间的映射关系。本章将深入探讨路由配置的各种选项和高级用法。

路由配置基础

一个基本的 Vue Router 配置包含一个路由数组,每个路由对象定义了路径与组件的映射关系。

基础配置最简单的路由配置

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

// 定义路由组件(实际开发中应该导入具体的组件文件)
const Home = { template: '<div>首页</div>' }
const About = { template: '<div>关于我们</div>' }

// 定义路由配置
const routes = [
  {
    path: '/',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]

// 创建路由实例
const router = createRouter({
  // 使用 HTML5 History 模式
  history: createWebHistory(),
  // 路由配置数组
  routes
})

export default router
                        

路由配置选项

每个路由对象可以包含多个配置选项,下面是最常用的配置项:

配置项 类型 说明 示例
path string 路由路径,支持动态参数和正则表达式 '/user/:id'
component Component 路由组件(默认视图) Home
name string 路由名称,用于编程式导航 'home'
components Object 命名视图组件映射 { default: Home, sidebar: Sidebar }
redirect string/Object/Function 重定向到其他路由 '/home'{ name: 'home' }
alias string/Array 路由别名 '/homepage'
meta Object 路由元信息,用于权限控制等 { requiresAuth: true }
props boolean/Object/Function 将路由参数作为 props 传递给组件 true{ default: true }
beforeEnter Function/Array 路由独享的守卫 守卫函数
children Array 嵌套路由配置 子路由数组

完整路由配置示例

完整示例包含所有常用选项的路由配置

import { createRouter, createWebHistory } from 'vue-router'

// 导入组件
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import UserProfile from '../views/UserProfile.vue'
import UserPosts from '../views/UserPosts.vue'
import NotFound from '../views/NotFound.vue'
import DashboardLayout from '../layouts/Dashboard.vue'
import DashboardHome from '../views/dashboard/Home.vue'
import DashboardSettings from '../views/dashboard/Settings.vue'

const routes = [
  // 基础路由
  {
    path: '/',
    name: 'home',
    component: Home,
    meta: {
      title: '首页',
      requiresAuth: false
    }
  },

  // 带别名的路由
  {
    path: '/about',
    name: 'about',
    component: About,
    alias: '/about-us', // 访问 /about-us 也会显示 About 组件
    meta: {
      title: '关于我们',
      requiresAuth: false
    }
  },

  // 重定向
  {
    path: '/home',
    redirect: '/' // 将 /home 重定向到 /
  },
  {
    path: '/old-about',
    redirect: { name: 'about' } // 命名路由重定向
  },
  {
    path: '/legacy/:id',
    redirect: to => {
      // 函数式重定向,可以动态计算重定向目标
      return { path: `/user/${to.params.id}` }
    }
  },

  // 动态路由
  {
    path: '/user/:id',
    name: 'user',
    component: UserProfile,
    props: true, // 将路由参数作为 props 传递
    meta: {
      requiresAuth: true,
      title: '用户资料'
    },
    // 路由独享守卫
    beforeEnter: (to, from) => {
      // 检查用户权限等
      if (!isValidUserId(to.params.id)) {
        return { name: 'not-found' }
      }
    }
  },

  // 嵌套路由
  {
    path: '/dashboard',
    component: DashboardLayout,
    meta: { requiresAuth: true },
    children: [
      // 当 /dashboard 时,默认显示 DashboardHome
      {
        path: '',
        name: 'dashboard',
        component: DashboardHome,
        meta: { title: '控制面板' }
      },
      {
        path: 'settings',
        name: 'dashboard.settings',
        component: DashboardSettings,
        meta: { title: '设置' }
      },
      // 嵌套的动态路由
      {
        path: 'user/:userId/posts/:postId',
        name: 'user.post',
        component: UserPosts,
        props: route => ({
          userId: parseInt(route.params.userId),
          postId: parseInt(route.params.postId),
          editMode: route.query.edit === 'true'
        })
      }
    ]
  },

  // 404 页面 - 通配符路由
  {
    path: '/:pathMatch(.*)*',
    name: 'not-found',
    component: NotFound,
    meta: { title: '页面未找到' }
  }
]

// 创建路由实例
const router = createRouter({
  history: createWebHistory(),
  routes,
  // 全局配置
  scrollBehavior(to, from, savedPosition) {
    // 控制滚动行为
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  }
})

export default router

// 辅助函数
function isValidUserId(id) {
  // 验证用户ID的逻辑
  return /^\d+$/.test(id)
}
                        

路径语法详解

Vue Router 的路径语法非常灵活,支持多种匹配模式。

# 基础路径
'/' // 匹配根路径
'/about' // 匹配 /about

# 动态参数
'/user/:id' // 匹配 /user/123, /user/abc
'/user/:id/post/:postId' // 多个参数

# 可选参数
'/archive/:year?/:month?/:day?' // ? 表示参数可选

# 正则约束
'/user/:id(\\d+)' // 只匹配数字ID
'/user/:username([a-z]+)' // 只匹配小写字母

# 重复参数
'/tags/:tags+' // + 表示一个或多个参数,匹配 /tags/vue/router
'/sections/:sections*' // * 表示零个或多个参数

# 通配符
'/:pathMatch(.*)*' // 匹配所有路径(用于404页面)

动态路由配置

动态路由允许根据参数动态匹配路由,是最常用的路由功能之一。

基本动态路由

示例动态路由的基本配置

const routes = [
  // 基础动态路由
  {
    path: '/user/:id',
    component: UserDetail
  },

  // 多个动态参数
  {
    path: '/category/:categoryId/product/:productId',
    component: ProductDetail
  },

  // 可选参数
  {
    path: '/blog/:year?/:month?/:day?',
    component: BlogArchive,
    // 当所有参数都省略时,显示所有博客文章
  },

  // 带正则约束的参数
  {
    path: '/user/:id(\\d+)', // 只匹配数字ID
    component: UserDetail
  },
  {
    path: '/file/:name(.+)?', // 匹配文件名,包括扩展名
    component: FileViewer
  }
]
                        

访问动态参数

示例在组件中访问路由参数

<!-- UserDetail.vue -->
<template>
  <div>
    <h2>用户详情</h2>
    <p>用户ID: {{ userId }}</p>
    <p>路由参数: {{ $route.params }}</p>
    <p>查询参数: {{ $route.query }}</p>
  </div>
</template>

<script>
// Options API
export default {
  computed: {
    userId() {
      return this.$route.params.id
    }
  },
  mounted() {
    console.log('路由参数:', this.$route.params)
    console.log('查询参数:', this.$route.query)
  }
}
</script>

<!-- Composition API -->
<script setup>
import { useRoute } from 'vue-router'

const route = useRoute()
const userId = route.params.id

// 监听参数变化
import { watch } from 'vue'
watch(
  () => route.params.id,
  (newId) => {
    console.log('用户ID变化:', newId)
    // 重新加载用户数据
    fetchUserData(newId)
  }
)
</script>
                        

使用 props 传递参数

通过配置 props 选项,可以将路由参数作为组件的 props 传递,使组件更易于测试和复用。

示例props 配置的三种方式

const routes = [
  // 1. 布尔模式:将 params 转换为 props
  {
    path: '/user/:id',
    component: UserDetail,
    props: true // 将 route.params.id 作为 id prop 传递
  },

  // 2. 对象模式:静态 props
  {
    path: '/about',
    component: About,
    props: {
      showHeader: true,
      theme: 'dark'
    }
  },

  // 3. 函数模式:动态计算 props
  {
    path: '/search',
    component: SearchResults,
    props: (route) => ({
      query: route.query.q || '',
      page: parseInt(route.query.page) || 1,
      sortBy: route.query.sort || 'relevance'
    })
  },

  // 4. 命名视图的 props 配置
  {
    path: '/dashboard',
    components: {
      default: Dashboard,
      sidebar: Sidebar
    },
    props: {
      default: true, // 为默认视图启用 props
      sidebar: (route) => ({
        activeTab: route.query.tab || 'overview'
      })
    }
  }
]
                        
示例在组件中接收 props

<!-- UserDetail.vue -->
<template>
  <div>
    <h2>用户 {{ id }} 的详情</h2>
    <!-- 直接使用 props -->
  </div>
</template>

<script>
// Options API
export default {
  props: {
    id: {
      type: [String, Number],
      required: true
    }
  }
}
</script>

<!-- Composition API -->
<script setup>
// 使用 defineProps 接收 props
const props = defineProps({
  id: {
    type: [String, Number],
    required: true
  }
})

console.log('用户ID:', props.id)
</script>
                        

嵌套路由配置

嵌套路由允许在组件内部嵌套其他路由,用于构建复杂的页面布局。

示例嵌套路由的完整配置

const routes = [
  {
    path: '/dashboard',
    component: DashboardLayout,
    meta: { requiresAuth: true },
    children: [
      // 空路径子路由 - 当访问 /dashboard 时显示
      {
        path: '',
        name: 'dashboard.home',
        component: DashboardHome,
        meta: { title: '控制面板' }
      },

      // 相对路径子路由 - 实际路径为 /dashboard/profile
      {
        path: 'profile',
        name: 'dashboard.profile',
        component: UserProfile,
        meta: { title: '个人资料' }
      },

      // 绝对路径子路由 - 实际路径为 /settings
      {
        path: '/settings',
        name: 'settings',
        component: Settings,
        meta: { title: '设置' }
      },

      // 嵌套的子路由 - 实际路径为 /dashboard/posts
      {
        path: 'posts',
        component: PostsLayout,
        children: [
          {
            path: '',
            component: PostsList,
            meta: { title: '文章列表' }
          },
          {
            path: ':postId',
            component: PostDetail,
            props: true,
            meta: { title: '文章详情' }
          },
          {
            path: 'create',
            component: CreatePost,
            meta: { title: '创建文章' }
          }
        ]
      }
    ]
  }
]
                        
路由结构示例:
/dashboard
├── /dashboard (空路径,显示 DashboardHome)
├── /dashboard/profile
├── /settings (绝对路径,不受父路径影响)
└── /dashboard/posts
├── /dashboard/posts (显示 PostsList)
├── /dashboard/posts/:postId
└── /dashboard/posts/create

命名视图配置

命名视图允许在同一路由下显示多个组件,适用于复杂的布局需求。

示例命名视图配置

<!-- App.vue 模板 -->
<template>
  <div id="app">
    <router-view name="header"></router-view>
    <main>
      <router-view></router-view> <!-- 默认视图 -->
    </main>
    <router-view name="sidebar"></router-view>
    <router-view name="footer"></router-view>
  </div>
</template>

<!-- 路由配置 -->
<script>
const routes = [
  {
    path: '/',
    // 注意:这里是 components(复数),不是 component
    components: {
      default: Home,           // 对应 <router-view>
      header: AppHeader,       // 对应 <router-view name="header">
      sidebar: AppSidebar,     // 对应 <router-view name="sidebar">
      footer: AppFooter        // 对应 <router-view name="footer">
    },
    meta: { title: '首页' }
  },
  {
    path: '/dashboard',
    components: {
      default: Dashboard,
      header: DashboardHeader,
      sidebar: DashboardSidebar,
      footer: DashboardFooter
    },
    meta: { requiresAuth: true }
  },
  {
    path: '/login',
    // 只覆盖部分命名视图
    components: {
      default: Login,
      header: null,  // 不显示 header
      footer: null   // 不显示 footer
    }
  }
]
</script>
                        

重定向和别名

重定向配置

示例重定向的三种方式

const routes = [
  // 1. 字符串重定向
  {
    path: '/home',
    redirect: '/'  // 将 /home 重定向到根路径
  },

  // 2. 命名路由重定向
  {
    path: '/old-user/:id',
    redirect: {
      name: 'user',  // 重定向到名为 'user' 的路由
      params: { id: 'new-id' }  // 可以修改参数
    }
  },

  // 3. 函数式重定向(最灵活)
  {
    path: '/legacy/:path*',
    redirect: to => {
      // 动态计算重定向目标
      const { params, query } = to

      if (params.path === 'about') {
        return { name: 'about' }
      } else if (params.path === 'contact') {
        return { path: '/contact', query: { from: 'legacy' } }
      } else {
        // 重定向到 404
        return { name: 'not-found' }
      }
    }
  },

  // 4. 相对重定向
  {
    path: '/users/:id',
    children: [
      {
        path: '',
        component: UserProfile
      },
      {
        path: 'posts',
        redirect: 'posts/list'  // 相对路径,重定向到 /users/:id/posts/list
      },
      {
        path: 'posts/list',
        component: UserPosts
      }
    ]
  }
]
                        

别名配置

示例别名的使用

const routes = [
  // 单个别名
  {
    path: '/about',
    component: About,
    alias: '/about-us'  // 访问 /about-us 也会显示 About 组件
  },

  // 多个别名
  {
    path: '/home',
    component: Home,
    alias: ['/index', '/main', '/welcome']  // 多个别名
  },

  // 嵌套路由的别名
  {
    path: '/user/:id',
    component: UserLayout,
    children: [
      {
        path: 'profile',
        component: UserProfile,
        alias: ['/u/:id', '/member/:id']  // 别名可以包含参数
      }
    ]
  },

  // 绝对别名
  {
    path: '/admin',
    component: Admin,
    alias: '/administrator'  // 绝对路径别名
  }
]
                        
重定向 vs 别名
  • 重定向:访问 A 时,URL 会变为 B,然后显示 B 对应的组件
  • 别名:访问 A 时,URL 保持不变,但显示 B 对应的组件

路由配置模块化

对于大型项目,建议将路由配置拆分为多个模块,便于管理和维护。

示例模块化路由配置

// router/index.js - 主路由文件
import { createRouter, createWebHistory } from 'vue-router'

// 导入模块化路由配置
import authRoutes from './routes/auth'
import adminRoutes from './routes/admin'
import userRoutes from './routes/user'
import blogRoutes from './routes/blog'

// 基础路由
const baseRoutes = [
  {
    path: '/',
    name: 'home',
    component: () => import('@/views/Home.vue'),
    meta: { title: '首页' }
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('@/views/About.vue'),
    meta: { title: '关于我们' }
  }
]

// 合并所有路由
const routes = [
  ...baseRoutes,
  ...authRoutes,
  ...adminRoutes,
  ...userRoutes,
  ...blogRoutes,
  // 404 路由放在最后
  {
    path: '/:pathMatch(.*)*',
    name: 'not-found',
    component: () => import('@/views/NotFound.vue'),
    meta: { title: '页面未找到' }
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router
                        
示例模块化路由文件示例

// router/routes/auth.js - 认证相关路由
const authRoutes = [
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/auth/Login.vue'),
    meta: {
      title: '登录',
      guestOnly: true  // 仅允许未登录用户访问
    }
  },
  {
    path: '/register',
    name: 'register',
    component: () => import('@/views/auth/Register.vue'),
    meta: {
      title: '注册',
      guestOnly: true
    }
  },
  {
    path: '/forgot-password',
    name: 'forgot-password',
    component: () => import('@/views/auth/ForgotPassword.vue'),
    meta: { title: '忘记密码' }
  },
  {
    path: '/reset-password/:token',
    name: 'reset-password',
    component: () => import('@/views/auth/ResetPassword.vue'),
    props: true,
    meta: { title: '重置密码' }
  }
]

export default authRoutes
                        

高级配置技巧

1. 路由配置验证

技巧使用 TypeScript 增强路由配置

// router/types.ts - 定义路由元信息类型
import 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    // 页面标题
    title?: string
    // 是否需要登录
    requiresAuth?: boolean
    // 仅允许未登录用户访问
    guestOnly?: boolean
    // 允许访问的角色
    roles?: string[]
    // 是否保持 alive
    keepAlive?: boolean
    // 面包屑
    breadcrumb?: string | ((route: RouteLocationNormalized) => string)
    // 页面图标
    icon?: string
  }
}

// router/index.ts - 使用类型检查的路由配置
import { RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
  {
    path: '/dashboard',
    name: 'dashboard',
    component: () => import('@/views/Dashboard.vue'),
    meta: {
      title: '控制面板',
      requiresAuth: true,
      roles: ['admin', 'editor'],
      icon: 'dashboard'
    }
  }
  // TypeScript 会检查 meta 属性的类型
]
                        

2. 动态添加路由

技巧动态添加路由配置

// 在应用运行时动态添加路由
import { useRouter } from 'vue-router'

const router = useRouter()

// 添加单个路由
router.addRoute({
  path: '/new-page',
  component: () => import('@/views/NewPage.vue'),
  meta: { title: '新页面' }
})

// 添加嵌套路由
router.addRoute('dashboard', {
  path: 'analytics',
  component: () => import('@/views/dashboard/Analytics.vue'),
  meta: { title: '数据分析' }
})

// 删除路由
const removeRoute = router.addRoute({
  path: '/temp',
  component: () => import('@/views/Temp.vue')
})
// 稍后删除
removeRoute()

// 或者通过名称删除
router.removeRoute('temp-route')

// 检查路由是否存在
const hasRoute = router.hasRoute('dashboard')

// 获取所有路由记录
const routes = router.getRoutes()
console.log('所有路由:', routes)
                        

路由配置演示

路由配置效果演示

当前路由:/

欢迎来到首页

路由配置演示中...

元信息:

路由配置最佳实践

路由配置最佳实践
  • 使用命名路由:提高代码可维护性,避免硬编码路径
  • 模块化配置:按功能拆分路由配置,便于管理
  • 合理使用懒加载:提高应用性能,按需加载组件
  • 统一元信息规范:定义统一的 meta 字段,便于权限控制
  • 参数验证:对动态路由参数进行验证,避免错误
  • 使用 props:通过 props 传递路由参数,提高组件复用性
  • 配置 404 路由:始终配置通配符路由处理未匹配的路径

总结

Vue Router 的路由配置提供了丰富的选项和灵活的语法,可以满足各种复杂的路由需求。通过合理配置路由,可以构建出结构清晰、易于维护的单页面应用。

关键要点回顾:

  • 基础配置:掌握 path、component、name 等基本配置项
  • 动态路由:使用动态参数和正则表达式匹配灵活的路由
  • 嵌套路由:通过 children 配置构建复杂的页面布局
  • 命名视图:使用多个 router-view 实现复杂布局
  • 重定向和别名:灵活处理 URL 映射关系
  • 模块化配置:拆分路由配置,提高可维护性

合理运用这些配置技巧,可以让你的 Vue 应用拥有更加强大和灵活的路由功能。

测试你的理解

思考题
  1. 如何在路由配置中定义动态参数?如何访问这些参数?
  2. props: true 配置有什么作用?它有哪些使用方式?
  3. 嵌套路由中,空路径子路由有什么特殊用途?
  4. 重定向和别名有什么区别?分别在什么场景下使用?
  5. 如何实现模块化的路由配置?有什么好处?