当前位置: 首页 > news >正文

【Vue】路由管理(Vue Router)

在这里插入图片描述

个人主页:Guiat
归属专栏:Vue

在这里插入图片描述

文章目录

  • 1. Vue Router 简介
    • 1.1 单页应用与路由
    • 1.2 Vue Router 的核心功能
  • 2. Vue Router 基础配置
    • 2.1 安装与基本设置
    • 2.2 路由模式
    • 2.3 路由组件与视图
  • 3. 路由导航
    • 3.1 声明式导航
    • 3.2 编程式导航
    • 3.3 路由参数获取
  • 4. 高级路由配置
    • 4.1 嵌套路由
    • 4.2 命名视图
    • 4.3 路由懒加载
  • 5. 路由守卫
    • 5.1 全局守卫
    • 5.2 路由独享守卫
    • 5.3 组件内守卫
  • 6. 路由元信息与数据获取
    • 6.1 路由元信息
    • 6.2 路由数据获取
  • 7. 动态路由与进阶技巧
    • 7.1 动态添加和删除路由
    • 7.2 路由过渡效果
    • 7.3 滚动行为
  • 8. 实际应用与最佳实践
    • 8.1 路由组织与模块化
    • 8.2 权限控制与认证
    • 8.3 性能优化策略
  • 9. Vue Router 与状态管理的结合
    • 9.1 Vue Router 与 Vuex/Pinia 协作
    • 9.2 路由参数同步到状态
    • 9.3 基于状态的导航守卫
  • 10. 测试与调试
    • 10.1 路由单元测试
    • 10.2 路由调试技巧
    • 10.3 常见问题与解决方案

正文

1. Vue Router 简介

Vue Router 是 Vue.js 官方的路由管理器,它与 Vue.js 核心深度集成,使构建单页面应用变得轻而易举。它提供了声明式的导航控制和组件系统,让开发者能够通过配置路由映射、嵌套路由等方式构建复杂的应用。

1.1 单页应用与路由

单页应用(SPA)是一种网页应用程序或网站的模型,它通过动态重写当前页面来与用户交互,而非传统的从服务器加载整个新页面。这种方法避免了页面之间切换打断用户体验。

// 传统多页应用
// 每次导航都会向服务器请求新页面
location.href = '/about.html'// 单页应用
// 只改变 URL,不重新加载页面
history.pushState(null, '', '/about')

1.2 Vue Router 的核心功能

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式
  • 自定义的滚动行为

2. Vue Router 基础配置

2.1 安装与基本设置

# Vue 2
npm install vue-router@3# Vue 3
npm install vue-router@4

基本配置(Vue 3):

// 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(),routes
})export default router

在 Vue 应用中使用:

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'const app = createApp(App)
app.use(router)
app.mount('#app')

2.2 路由模式

Vue Router 提供两种路由模式:

  1. Hash 模式:使用 URL 的 hash (#) 来模拟完整的 URL
  2. History 模式:利用 HTML5 History API 实现无 hash 的 URL
// Hash 模式 (默认)
const router = createRouter({history: createWebHashHistory(),routes
})// History 模式
const router = createRouter({history: createWebHistory(),routes
})

2.3 路由组件与视图

在 Vue 组件中使用路由视图:

<template><div id="app"><nav><router-link to="/">Home</router-link> |<router-link to="/about">About</router-link></nav><!-- 路由匹配的组件将渲染在这里 --><router-view /></div>
</template>

3. 路由导航

3.1 声明式导航

使用 <router-link> 组件进行声明式导航:

<!-- 基本用法 -->
<router-link to="/">Home</router-link><!-- 命名路由 -->
<router-link :to="{ name: 'About' }">About</router-link><!-- 带参数的路由 -->
<router-link :to="{ name: 'User', params: { id: 123 }}">User</router-link><!-- 带查询参数 -->
<router-link :to="{ path: '/search', query: { q: 'vue' }}">Search</router-link><!-- 自定义激活类 -->
<router-link to="/about" active-class="active">About</router-link><!-- 精确匹配 (Vue 3) -->
<router-link to="/" :exact="true">Home</router-link>

3.2 编程式导航

通过 JavaScript 代码控制路由:

// 基本导航
this.$router.push('/')
this.$router.push('/about')// 对象形式
this.$router.push({ path: '/about' })// 命名路由
this.$router.push({ name: 'User', params: { id: 123 }})// 带查询参数
this.$router.push({ path: '/search', query: { q: 'vue' }})// 替换当前历史记录
this.$router.replace('/about')// 前进/后退
this.$router.go(1)   // 前进一步
this.$router.go(-1)  // 后退一步
this.$router.back()  // 后退一步
this.$router.forward() // 前进一步

在 Vue 3 Composition API 中:

import { useRouter } from 'vue-router'export default {setup() {const router = useRouter()function navigateToAbout() {router.push('/about')}return { navigateToAbout }}
}

3.3 路由参数获取

通过 $route 对象访问路由参数:

// 路由配置
const routes = [{path: '/user/:id',name: 'User',component: User}
]// 在组件中获取参数 (Vue 2)
export default {created() {// 路径参数console.log(this.$route.params.id)// 查询参数console.log(this.$route.query.search)}
}// 在组件中获取参数 (Vue 3 Composition API)
import { useRoute } from 'vue-router'export default {setup() {const route = useRoute()console.log(route.params.id)console.log(route.query.search)}
}

4. 高级路由配置

4.1 嵌套路由

路由可以嵌套,实现复杂的应用布局:

const routes = [{path: '/user',component: User,children: [// 当 /user 匹配成功// UserHome 会被渲染在 User 的 <router-view> 中{ path: '', component: UserHome },// 当 /user/profile 匹配成功// UserProfile 会被渲染在 User 的 <router-view> 中{ path: 'profile', component: UserProfile },// 当 /user/posts 匹配成功// UserPosts 会被渲染在 User 的 <router-view> 中{ path: 'posts', component: UserPosts }]}
]

对应的组件结构:

<!-- User.vue -->
<template><div><h2>User Section</h2><nav><router-link to="/user">Home</router-link><router-link to="/user/profile">Profile</router-link><router-link to="/user/posts">Posts</router-link></nav><router-view></router-view></div>
</template>

4.2 命名视图

同一个路由可以显示多个视图:

const routes = [{path: '/',components: {default: Home,sidebar: Sidebar,footer: Footer}}
]

对应的模板:

<router-view></router-view>
<router-view name="sidebar"></router-view>
<router-view name="footer"></router-view>

4.3 路由懒加载

为了提高应用性能,可以使用动态导入实现路由懒加载:

// 不使用懒加载
import UserDetails from './views/UserDetails.vue'const routes = [{ path: '/user/:id', component: UserDetails }
]// 使用懒加载
const routes = [{ path: '/user/:id', component: () => import('./views/UserDetails.vue') }
]// 分组懒加载
const routes = [{path: '/user',component: () => import(/* webpackChunkName: "user" */ './views/User.vue'),children: [{path: 'profile',component: () => import(/* webpackChunkName: "user" */ './views/UserProfile.vue')},{path: 'posts',component: () => import(/* webpackChunkName: "user" */ './views/UserPosts.vue')}]}
]

5. 路由守卫

5.1 全局守卫

全局前置守卫:

router.beforeEach((to, from, next) => {// to: 即将进入的目标路由对象// from: 当前导航正要离开的路由对象// next: 函数,必须调用该方法来 resolve 这个钩子// 检查用户是否已登录if (to.meta.requiresAuth && !isAuthenticated) {// 未登录,重定向到登录页next({ name: 'Login' })} else {// 继续导航next()}
})

全局解析守卫:

router.beforeResolve((to, from, next) => {// 在所有组件内守卫和异步路由组件被解析之后调用next()
})

全局后置钩子:

router.afterEach((to, from) => {// 不会影响导航本身,常用于分析、更改页面标题等document.title = to.meta.title || 'My App'
})

5.2 路由独享守卫

直接在路由配置上定义:

const routes = [{path: '/admin',component: Admin,beforeEnter: (to, from, next) => {// 检查用户权限if (hasAdminRights()) {next()} else {next('/403')}}}
]

5.3 组件内守卫

在组件内定义路由导航守卫:

export default {// 在渲染该组件的对应路由被验证前调用beforeRouteEnter(to, from, next) {// 不能获取组件实例 `this`// 因为当守卫执行时,组件实例还没被创建next(vm => {// 通过 `vm` 访问组件实例})},// 在当前路由改变,但是该组件被复用时调用beforeRouteUpdate(to, from, next) {// 可以访问组件实例 `this`this.name = to.params.namenext()},// 导航离开该组件的对应路由时调用beforeRouteLeave(to, from, next) {// 可以访问组件实例 `this`const answer = window.confirm('Do you really want to leave?')if (answer) {next()} else {next(false)}}
}

在 Vue 3 Composition API 中:

import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'export default {setup() {// 与 beforeRouteLeave 相同onBeforeRouteLeave((to, from) => {const answer = window.confirm('Do you really want to leave?')if (!answer) return false})// 与 beforeRouteUpdate 相同onBeforeRouteUpdate((to, from) => {// 处理路由变化...})}
}

6. 路由元信息与数据获取

6.1 路由元信息

可以在路由配置中附加自定义数据:

const routes = [{path: '/admin',component: Admin,meta: { requiresAuth: true,title: 'Admin Panel',permissions: ['admin', 'editor']}}
]

使用元信息:

// 全局守卫中使用
router.beforeEach((to, from, next) => {document.title = to.meta.title || 'Default Title'if (to.meta.requiresAuth && !isAuthenticated()) {next('/login')} else {next()}
})// 组件中使用
export default {created() {console.log(this.$route.meta.permissions)}
}

6.2 路由数据获取

在导航完成前获取数据:

export default {data() {return {post: null,loading: false,error: null}},beforeRouteEnter(to, from, next) {next(vm => {vm.fetchData()})},beforeRouteUpdate(to, from, next) {this.post = nullthis.fetchData()next()},methods: {fetchData() {this.loading = truethis.error = null// 假设 getPost 返回一个 PromisegetPost(this.$route.params.id).then(post => {this.post = postthis.loading = false}).catch(err => {this.error = err.toString()this.loading = false})}}
}

在导航完成后获取数据:

export default {data() {return {post: null,loading: false,error: null}},created() {// 组件创建完后获取数据this.fetchData()},watch: {// 路由变化时重新获取数据'$route': 'fetchData'},methods: {fetchData() {this.loading = truethis.error = nullgetPost(this.$route.params.id).then(post => {this.post = postthis.loading = false}).catch(err => {this.error = err.toString()this.loading = false})}}
}

在 Vue 3 Composition API 中:

import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'export default {setup() {const route = useRoute()const post = ref(null)const loading = ref(false)const error = ref(null)const fetchData = async () => {loading.value = trueerror.value = nulltry {post.value = await getPost(route.params.id)} catch (err) {error.value = err.toString()} finally {loading.value = false}}// 首次加载fetchData()// 路由参数变化时重新获取watch(() => route.params.id, fetchData)return { post, loading, error }}
}

7. 动态路由与进阶技巧

7.1 动态添加和删除路由

Vue Router 允许动态修改路由配置:

// 添加路由
router.addRoute({path: '/dynamic',name: 'Dynamic',component: () => import('./views/Dynamic.vue')
})// 添加嵌套路由
router.addRoute('User', {path: 'settings',component: UserSettings
})// 删除路由
// 方法 1:添加一个名字相同的路由
router.addRoute({ path: '/about', name: 'About', component: About })// 方法 2:通过返回的删除函数
const removeRoute = router.addRoute(route)
removeRoute() // 删除路由// 方法 3:通过名称删除
router.removeRoute('About')// 检查路由是否存在
router.hasRoute('About')// 获取所有路由记录
router.getRoutes()

7.2 路由过渡效果

结合 Vue 的过渡系统实现路由切换动画:

<template><router-view v-slot="{ Component }"><transition name="fade" mode="out-in"><component :is="Component" /></transition></router-view>
</template><style>
.fade-enter-active,
.fade-leave-active {transition: opacity 0.3s ease;
}.fade-enter-from,
.fade-leave-to {opacity: 0;
}
</style>

基于路由的动态过渡:

<template><router-view v-slot="{ Component, route }"><transition :name="route.meta.transition || 'fade'"><component :is="Component" /></transition></router-view>
</template><script>
export default {// 路由配置// {//   path: '/slide-page',//   component: SlidePage,//   meta: { transition: 'slide' }// }
}
</script><style>
.fade-enter-active,
.fade-leave-active {transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {opacity: 0;
}.slide-enter-active,
.slide-leave-active {transition: transform 0.3s;
}
.slide-enter-from,
.slide-leave-to {transform: translateX(100%);
}
</style>

7.3 滚动行为

控制页面切换时的滚动位置:

const router = createRouter({history: createWebHistory(),routes: [...],scrollBehavior(to, from, savedPosition) {// 返回 savedPosition,在按下 后退/前进 按钮时有效if (savedPosition) {return savedPosition}// 滚动到锚点if (to.hash) {return {el: to.hash,behavior: 'smooth',}}// 滚动到顶部return { top: 0 }}
})

8. 实际应用与最佳实践

8.1 路由组织与模块化

大型应用中的路由组织:

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import homeRoutes from './modules/home'
import userRoutes from './modules/user'
import shopRoutes from './modules/shop'const routes = [...homeRoutes,...userRoutes,...shopRoutes,{ path: '/:pathMatch(.*)*', name: 'NotFound', component: () => import('../views/NotFound.vue') }
]const router = createRouter({history: createWebHistory(),routes
})export default router// router/modules/user.js
export default [{path: '/user',component: () => import('@/layouts/UserLayout.vue'),children: [{path: '',name: 'UserHome',component: () => import('@/views/user/Home.vue')},{path: 'profile',name: 'UserProfile',component: () => import('@/views/user/Profile.vue')}]}
]

8.2 权限控制与认证

基于角色的路由访问控制:

// 路由配置
const routes = [{path: '/admin',component: Admin,meta: { requiresAuth: true, roles: ['admin'] }},{path: '/editor',component: Editor,meta: { requiresAuth: true, roles: ['editor', 'admin'] }}
]// 全局前置守卫
router.beforeEach((to, from, next) => {const { requiresAuth, roles } = to.metaconst currentUser = getCurrentUser()if (!requiresAuth) {return next()}if (!currentUser) {return next({path: '/login',query: { redirect: to.fullPath }})}if (roles && !roles.includes(currentUser.role)) {return next('/403')}next()
})

8.3 性能优化策略

  1. 路由懒加载:减小初始加载体积
const routes = [{path: '/dashboard',component: () => import('./views/Dashboard.vue')}
]
  1. 预加载关键路由:提高用户体验
// 在应用空闲时预加载
const PreloadedView = () => ({component: import('./views/PreloadedView.vue'),loading: LoadingComponent,delay: 200
})
  1. 保持路由组件状态:使用 <keep-alive>
<router-view v-slot="{ Component }"><keep-alive><component :is="Component" /></keep-alive>
</router-view>
  1. 路由元信息缓存控制
const routes = [{path: '/user/:id',component: UserDetails,meta: { keepAlive: true }}
]// 在模板中
<router-view v-slot="{ Component }"><keep-alive><component v-if="$route.meta.keepAlive" :is="Component" /></keep-alive><component v-if="!$route.meta.keepAlive" :is="Component" />
</router-view>

9. Vue Router 与状态管理的结合

9.1 Vue Router 与 Vuex/Pinia 协作

// store/index.js (Vuex)
export default createStore({state: {user: null,isAuthenticated: false},mutations: {setUser(state, user) {state.user = userstate.isAuthenticated = !!user}},actions: {async login({ commit }, credentials) {const user = await authService.login(credentials)commit('setUser', user)return user},async logout({ commit }) {await authService.logout()commit('setUser', null)}}
})// 路由守卫中使用
router.beforeEach((to, from, next) => {const requiresAuth = to.matched.some(record => record.meta.requiresAuth)const { isAuthenticated } = store.stateif (requiresAuth && !isAuthenticated) {next('/login')} else {next()}
})

9.2 路由参数同步到状态

// store/index.js
export default createStore({state: {searchQuery: '',currentProductId: null},mutations: {setSearchQuery(state, query) {state.searchQuery = query},setCurrentProductId(state, id) {state.currentProductId = id}}
})// 组件中
export default {created() {// 从路由同步到状态this.$store.commit('setSearchQuery', this.$route.query.q || '')this.$store.commit('setCurrentProductId', this.$route.params.id)},watch: {'$route'(to) {this.$store.commit('setSearchQuery', to.query.q || '')this.$store.commit('setCurrentProductId', to.params.id)}}
}

9.3 基于状态的导航守卫

router.beforeEach(async (to, from, next) => {// 检查是否需要获取用户信息if (to.matched.some(record => record.meta.requiresAuth) && store.state.user === null) {try {// 尝试获取用户信息await store.dispatch('fetchCurrentUser')next()} catch (error) {next('/login')}} else {next()}
})

10. 测试与调试

10.1 路由单元测试

使用 Vue Test Utils 测试路由组件:

import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
import App from '@/App.vue'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'// 创建路由实例
const routes = [{ path: '/', component: Home },{ path: '/about', component: About }
]const router = createRouter({history: createWebHistory(),routes
})test('routing', async () => {// 挂载应用const wrapper = mount(App, {global: {plugins: [router]}})// 等待路由准备就绪await router.isReady()// 验证初始路由expect(wrapper.html()).toContain('Welcome to Home')// 导航到 About 页面await router.push('/about')// 等待路由变化await wrapper.vm.$nextTick()// 验证新路由内容expect(wrapper.html()).toContain('About Page')
})

10.2 路由调试技巧

  1. 使用 Vue Devtools 检查路由状态

  2. 路由日志记录:

router.beforeEach((to, from, next) => {console.log(`Navigating from ${from.fullPath} to ${to.fullPath}`)next()
})
  1. 路由错误处理:
router.onError((error) => {console.error('Router error:', error)// 可以上报到错误监控系统
})

10.3 常见问题与解决方案

  1. 页面刷新 404 问题
    在使用 history 模式时,需要服务器配置将所有请求重定向到 index.html
# Nginx 配置
location / {try_files $uri $uri/ /index.html;
}
  1. 路由参数变化但组件不更新
    使用 watch 监听路由变化或使用 key 强制更新
<router-view :key="$route.fullPath"></router-view>
  1. 路由守卫中的异步操作
    确保在异步操作完成后调用 next()
router.beforeEach(async (to, from, next) => {try {await someAsyncOperation()next()} catch (error) {next(false)}
})
  1. 导航重复错误
    处理导航到当前位置的情况
// Vue Router 4.x
router.push('/current-path').catch(err => {if (err.name !== 'NavigationDuplicated') {throw err}
})// 或全局处理
const originalPush = router.push
router.push = function push(location) {return originalPush.call(this, location).catch(err => {if (err.name !== 'NavigationDuplicated') throw err})
}

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

http://www.xdnf.cn/news/88777.html

相关文章:

  • Java ByteBuf解析和进制转换汇总
  • Spark-SQL 项目
  • Linux安装后无法启动24天
  • 数据集 | 柑橘果目标检测数据集
  • 大数据开发的基本流程
  • 基于机器学习的房租影响因素分析系统
  • 安卓模拟器绕过检测全解析:雷电、MuMu、蓝叠、逍遥、夜神与WSA完整指南
  • 3.1.1 MaterialDesign中DrawerHost使用案例
  • Kubernetes Docker 部署达梦8数据库
  • 蓝桥杯算法实战分享:C/C++ 题型解析与实战技巧
  • 明远智睿2351开发板:四核1.4G处理器——开启高效能Linux系统新纪元
  • 『不废话』之Python管理工具uv快速入门
  • 【Java】Hibernate的检索策略
  • python的深拷贝浅拷贝(copy /deepcopy )
  • 三维几何变换
  • usb2.0的硬件知识(一)
  • 查看MySql操作日志
  • 布隆过滤器的应用
  • 《Operating System Concepts》阅读笔记:p764-p766
  • 【Axure视频教程】不透明度函数
  • 以下是一个基于 ESP32 - S3 实现消息队列收发测试的 C 例程
  • crontab 定时备份 mysql 数据库
  • CF思维题(cf round 1019 div.2 b题)
  • ADS基本操作之S参数仿真
  • 如何高效优化复杂的SQL查询:以项目发布管理为例
  • Java知识大纲
  • 内存管理之文件内存映射(mmap):外存(磁盘/flash)的文件映射到应用层(跨越内核层)
  • 解析芯片低功耗设计的底层逻辑与实现方法
  • 最新项目笔记
  • Java的反射机制(曼波超易懂图文版)