1. Vue3 路由基础概念
路由是现代前端框架的核心功能之一,它允许我们在单页面应用(SPA)中实现页面导航和视图切换。Vue3 的路由系统通过 vue-router 库实现,相比 Vue2 版本有了显著改进。
1.1 什么是前端路由
前端路由的本质是监听 URL 变化,根据不同的 URL 显示不同的组件内容,而不需要向服务器发起请求。这种机制带来了更流畅的用户体验,因为只有部分页面内容会更新。
在传统多页面应用中:
- 每次导航都会导致整个页面刷新
- 服务器需要返回完整的 HTML 文档
- 页面状态难以保持
而在单页面应用中:
- 只有必要的组件会动态更新
- 应用状态可以持久化
- 导航体验更加流畅
1.2 vue-router 的核心组成
vue-router 4.x(Vue3 版本)包含以下核心概念:
- 路由配置:定义 URL 路径与组件的映射关系
- 路由器实例:管理整个应用的路由状态
- 路由视图:
<router-view>组件,用于显示匹配的组件内容 - 导航链接:
<router-link>组件,用于声明式导航 - 路由守卫:控制导航过程的钩子函数
2. Vue3 路由配置详解
2.1 安装与基本配置
首先需要安装 vue-router:
npm install vue-router@4然后创建路由配置文件(通常为 router/index.js):
import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import About from '../views/About.vue' const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', component: About } ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router2.2 路由模式选择
Vue3 提供了两种主要的路由历史模式:
HTML5 模式(createWebHistory):
- 使用标准的 URL 路径(如 /about)
- 需要服务器配置支持
- 更美观的 URL
Hash 模式(createWebHashHistory):
- 使用 URL hash(如 /#/about)
- 不需要服务器特殊配置
- 兼容性更好
2.3 动态路由匹配
动态路由允许我们匹配不同模式的路径:
{ path: '/user/:id', component: User }在组件中可以通过useRoute()访问当前路由信息:
import { useRoute } from 'vue-router' export default { setup() { const route = useRoute() console.log(route.params.id) // 访问动态参数 } }3. 路由导航与视图渲染
3.1 声明式导航
使用<router-link>组件进行导航:
<router-link to="/">Home</router-link> <router-link to="/about">About</router-link>可以添加 active 类样式:
.router-link-active { font-weight: bold; }3.2 编程式导航
在组件方法中使用路由实例进行导航:
import { useRouter } from 'vue-router' export default { setup() { const router = useRouter() const navigate = () => { router.push('/about') // 或者 router.push({ name: 'About' }) } return { navigate } } }3.3 命名视图
对于复杂布局,可以使用命名视图:
{ path: '/settings', components: { default: Settings, sidebar: SettingsSidebar } }然后在模板中使用:
<router-view /> <router-view name="sidebar" />4. 高级路由功能
4.1 路由守卫
路由守卫用于控制导航流程:
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !isAuthenticated) { next('/login') } else { next() } })组件内守卫:
export default { beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被验证前调用 }, beforeRouteUpdate(to, from, next) { // 在当前路由改变,但是该组件被复用时调用 }, beforeRouteLeave(to, from, next) { // 在导航离开该组件的对应路由时调用 } }4.2 路由元信息
可以为路由添加自定义元数据:
{ path: '/admin', meta: { requiresAuth: true } }然后在导航守卫中访问这些元数据。
4.3 懒加载路由
使用动态导入实现路由懒加载:
{ path: '/dashboard', component: () => import('../views/Dashboard.vue') }4.4 滚动行为
自定义页面滚动行为:
const router = createRouter({ scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { top: 0 } } } })5. Vue3 路由最佳实践
5.1 项目结构建议
推荐的路由项目结构:
src/ router/ index.js # 主路由配置 routes/ # 路由模块 auth.js # 认证相关路由 admin.js # 管理后台路由 ...5.2 性能优化技巧
- 路由懒加载:拆分代码,按需加载
- 预加载:使用
<router-link>的 prefetch 功能 - 路由分组:将相关路由分组到同一 chunk
5.3 常见问题解决
页面刷新 404:
- HTML5 模式下需要服务器配置
- Nginx 示例配置:
location / { try_files $uri $uri/ /index.html; }
路由重复导航:
router.push('/').catch(() => {})动态路由参数变化不触发更新:
- 使用 watch 监听路由变化
- 或者使用组件内守卫 beforeRouteUpdate
6. Vue3 路由与状态管理
6.1 与 Pinia 集成
在路由守卫中使用 Pinia store:
import { useAuthStore } from '@/stores/auth' router.beforeEach((to) => { const auth = useAuthStore() if (to.meta.requiresAuth && !auth.isLoggedIn) { return '/login' } })6.2 路由状态持久化
保存路由状态到本地存储:
router.afterEach((to) => { localStorage.setItem('lastRoute', to.fullPath) })然后在应用初始化时恢复:
const lastRoute = localStorage.getItem('lastRoute') if (lastRoute) { router.push(lastRoute) }7. Vue3 路由实战案例
7.1 权限控制路由
实现基于角色的路由权限控制:
const routes = [ { path: '/admin', meta: { roles: ['admin'] } }, { path: '/user', meta: { roles: ['user', 'admin'] } } ] router.beforeEach((to, from, next) => { const userRole = getCurrentUserRole() if (to.meta.roles && !to.meta.roles.includes(userRole)) { next('/forbidden') } else { next() } })7.2 多标签页实现
使用 keep-alive 和 router-view 实现多标签页:
<router-view v-slot="{ Component }"> <keep-alive> <component :is="Component" :key="$route.fullPath" /> </keep-alive> </router-view>7.3 过渡动画
为路由切换添加动画效果:
<router-view v-slot="{ Component }"> <transition name="fade" mode="out-in"> <component :is="Component" /> </transition> </router-view>.fade-enter-active, .fade-leave-active { transition: opacity 0.3s ease; } .fade-enter-from, .fade-leave-to { opacity: 0; }8. Vue3 路由测试与调试
8.1 单元测试路由
使用 jest 测试路由导航:
import { mount } from '@vue/test-utils' import { createRouter, createWebHistory } from 'vue-router' import App from '@/App.vue' const router = createRouter({ history: createWebHistory(), routes: [{ path: '/', component: { template: '<div>Home</div>' } }] }) test('navigates to home', async () => { const wrapper = mount(App, { global: { plugins: [router] } }) await router.push('/') expect(wrapper.html()).toContain('Home') })8.2 路由调试技巧
- 使用 Vue Devtools 检查当前路由状态
- 添加路由导航日志:
router.afterEach((to, from) => { console.log(`Navigated from ${from.path} to ${to.path}`) }) - 使用路由元信息标记开发状态
9. Vue3 路由与 Composition API
9.1 使用组合式 API
在 setup 中使用路由相关功能:
import { useRouter, useRoute } from 'vue-router' export default { setup() { const router = useRouter() const route = useRoute() const navigate = () => { router.push({ path: '/user', query: { id: route.params.id } }) } return { navigate } } }9.2 自定义组合式路由函数
创建可复用的路由逻辑:
// composables/useNavigation.js import { useRouter } from 'vue-router' export function useNavigation() { const router = useRouter() const navigateToHome = () => router.push('/') const navigateBack = () => router.go(-1) return { navigateToHome, navigateBack } }10. Vue3 路由进阶模式
10.1 动态路由添加
运行时动态添加路由:
const newRoute = { path: '/new', component: NewComponent } router.addRoute(newRoute)10.2 路由模块联邦
结合 Module Federation 实现微前端路由集成:
// 主应用 router.addRoute({ path: '/remote/*', component: () => import('remoteApp/RemotePage') })10.3 服务端渲染 (SSR)
在 Nuxt.js 或自定义 SSR 中使用 Vue Router:
// server.js import { createSSRApp } from 'vue' import { createMemoryHistory } from 'vue-router' import App from './App.vue' import createRouter from './router' export async function createApp() { const app = createSSRApp(App) const router = createRouter(createMemoryHistory()) app.use(router) await router.isReady() return { app, router } }