Vue路由深度解析:Vue Router与导航守卫
一、Vue Router基础与安装配置
1. Vue Router核心概念
Vue Router是Vue.js官方的路由管理器,主要功能包括:
- 嵌套路由映射
- 模块化的路由配置
- 路由参数、查询、通配符
- 细粒度的导航控制
- 自动激活的CSS类链接
- HTML5 history模式或hash模式
- 可定制的滚动行为
2. 安装与基本配置
安装Vue Router:
npm install vue-router@4
# 或
yarn add vue-router@4
基础配置示例:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'const routes = [{path: '/',name: 'home',component: HomeView},{path: '/about',name: 'about',// 路由级代码拆分component: () => import('../views/AboutView.vue')}
]const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes
})export default router
在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')
二、路由模式与路由配置详解
1. 路由模式对比
模式 | 特点 | 示例 |
---|---|---|
Hash模式 | 使用URL hash模拟完整URL | http://example.com/#/about |
History模式 | 使用HTML5 History API | http://example.com/about |
Memory模式 | 不修改URL,适合非浏览器环境(如Electron) | 无URL变化 |
配置示例:
import { createWebHashHistory, // Hash模式createWebHistory, // History模式createMemoryHistory // Memory模式
} from 'vue-router'const router = createRouter({history: createWebHistory(), // 推荐生产环境使用// history: createWebHashHistory(), // 兼容性更好// history: createMemoryHistory(), // 非浏览器环境routes
})
2. 动态路由与参数传递
动态路由配置:
const routes = [// 动态字段以冒号开始{path: '/user/:id',name: 'user',component: UserView},// 可匹配/user/123或/user/456{path: '/user/:id/posts/:postId',component: UserPostView}
]
参数获取方式:
<template><!-- 在模板中直接使用 --><div>用户ID: {{ $route.params.id }}</div>
</template><script setup>
import { useRoute } from 'vue-router'// 在setup中使用
const route = useRoute()
console.log(route.params.id)
</script><script>
// 在Options API中使用
export default {created() {console.log(this.$route.params.id)}
}
</script>
3. 嵌套路由与命名视图
嵌套路由配置:
const routes = [{path: '/user/:id',component: UserLayout,children: [{path: '', // 默认子路由component: UserProfile},{path: 'posts',component: UserPosts},{path: 'settings',component: UserSettings}]}
]
命名视图(多路由出口):
const routes = [{path: '/',components: {default: HomeView, // 默认出口sidebar: SidebarView, // <router-view name="sidebar">footer: AppFooter // <router-view name="footer">}}
]
三、导航守卫全面解析
1. 导航守卫类型与执行流程
完整的导航解析流程:
- 导航被触发
- 调用
beforeRouteLeave
守卫(组件内) - 调用全局
beforeEach
守卫 - 在重用的组件里调用
beforeRouteUpdate
守卫(组件内) - 调用路由配置里的
beforeEnter
守卫 - 解析异步路由组件
- 在被激活的组件里调用
beforeRouteEnter
(组件内) - 调用全局
beforeResolve
守卫 - 导航被确认
- 调用全局
afterEach
钩子 - 触发DOM更新
- 调用
beforeRouteEnter
守卫中传给next
的回调函数
2. 全局守卫
// router/index.js// 全局前置守卫
router.beforeEach((to, from, next) => {console.log('全局前置守卫', to, from)// 必须调用next()继续导航next()
})// 全局解析守卫
router.beforeResolve((to, from, next) => {console.log('全局解析守卫', to, from)next()
})// 全局后置钩子
router.afterEach((to, from) => {console.log('全局后置钩子', to, from)// 不需要next函数
})
3. 路由独享守卫
const routes = [{path: '/admin',component: AdminView,beforeEnter: (to, from, next) => {// 仅在此路由触发if (isAdmin()) next()else next('/login')}}
]
4. 组件内守卫
<script>
export default {beforeRouteEnter(to, from, next) {// 在渲染该组件的对应路由被验证前调用// 不能获取组件实例 `this`next(vm => {// 通过 `vm` 访问组件实例console.log(vm.someData)})},beforeRouteUpdate(to, from) {// 当前路由改变,但是该组件被复用时调用// 可以访问组件实例 `this`this.fetchData(to.params.id)},beforeRouteLeave(to, from) {// 导航离开该组件的对应路由时调用// 可以访问组件实例 `this`const answer = window.confirm('确定要离开吗?未保存的更改将会丢失')if (!answer) return false}
}
</script>
5. 导航守卫实战:权限控制
// router/index.js
import { useAuthStore } from '@/stores/auth'const routes = [{path: '/',name: 'home',component: HomeView,meta: { requiresAuth: false }},{path: '/dashboard',name: 'dashboard',component: DashboardView,meta: { requiresAuth: true,roles: ['admin', 'editor'] }},{path: '/admin',name: 'admin',component: AdminView,meta: { requiresAuth: true,roles: ['admin'] }}
]router.beforeEach(async (to, from, next) => {const authStore = useAuthStore()const isAuthenticated = authStore.isAuthenticatedconst userRole = authStore.user?.role || 'guest'// 检查路由是否需要认证if (to.meta.requiresAuth && !isAuthenticated) {return next({ name: 'login', query: { redirect: to.fullPath } })}// 检查路由角色权限if (to.meta.roles && !to.meta.roles.includes(userRole)) {return next({ name: 'forbidden' })}// 如果用户已登录但要去登录页,重定向到首页if (to.name === 'login' && isAuthenticated) {return next({ name: 'home' })}next()
})
四、路由高级特性
1. 路由元信息与过渡动画
路由元信息配置:
const routes = [{path: '/posts',component: PostsLayout,meta: { requiresAuth: true,transition: 'slide-left' },children: [{path: 'new',component: NewPost,meta: { transition: 'slide-up',requiresAdmin: true }}]}
]
动态过渡效果:
<template><router-view v-slot="{ Component, route }"><transition :name="route.meta.transition || 'fade'"><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;
}.slide-left-enter-active,
.slide-left-leave-active {transition: transform 0.3s ease;
}.slide-left-enter-from {transform: translateX(100%);
}.slide-left-leave-to {transform: translateX(-100%);
}.slide-up-enter-active,
.slide-up-leave-active {transition: transform 0.3s ease;
}.slide-up-enter-from {transform: translateY(100%);
}.slide-up-leave-to {transform: translateY(-100%);
}
</style>
2. 滚动行为控制
const router = createRouter({history: createWebHistory(),routes,scrollBehavior(to, from, savedPosition) {// 返回滚动位置对象if (savedPosition) {return savedPosition // 浏览器前进/后退时恢复位置}if (to.hash) {return {el: to.hash, // 滚动到锚点behavior: 'smooth' // 平滑滚动}}if (to.meta.scrollToTop) {return { top: 0 } // 新路由滚动到顶部}// 默认不改变滚动位置}
})
3. 路由懒加载与分包
基础懒加载:
const routes = [{path: '/about',component: () => import('../views/AboutView.vue')}
]
自定义分包:
const routes = [{path: '/admin',component: () => import(/* webpackChunkName: "admin" */ '../views/AdminView.vue'),children: [{path: 'dashboard',component: () => import(/* webpackChunkName: "admin" */ '../views/AdminDashboard.vue')}]},{path: '/user/:id',component: () => import(/* webpackChunkName: "user" */ '../views/UserView.vue')}
]
4. 动态路由API
添加路由:
router.addRoute({path: '/new-route',name: 'newRoute',component: () => import('../views/NewView.vue')
})// 添加到现有路由的子路由
router.addRoute('parentRoute', {path: 'child',component: () => import('../views/ChildView.vue')
})
删除路由:
// 通过名称删除
router.removeRoute('routeName')// 通过添加返回的回调删除
const removeRoute = router.addRoute(routeConfig)
removeRoute() // 删除路由
检查路由:
// 检查路由是否存在
router.hasRoute('routeName')// 获取所有路由记录
router.getRoutes()
五、常见问题与最佳实践
1. 常见问题解决方案
问题1:路由重复跳转报错
// 统一处理导航错误
router.onError((error) => {if (error.message.includes('Avoided redundant navigation')) {// 忽略重复导航错误} else {// 处理其他导航错误}
})
问题2:动态路由刷新404
- History模式需要服务器配置支持
- Nginx配置示例:
location / {try_files $uri $uri/ /index.html; }
问题3:路由组件不更新
// 使用beforeRouteUpdate或监听$route
watch(() => route.params.id,(newId) => {fetchData(newId)}
)
2. 最佳实践建议
-
路由组织:
- 按功能模块组织路由文件
- 使用路由元信息(meta)存储权限、标题等信息
- 对大型项目考虑自动导入路由
-
性能优化:
- 合理使用路由懒加载
- 对频繁访问的路由考虑预加载
- 避免在导航守卫中进行繁重操作
-
安全实践:
- 始终验证前端路由权限
- 敏感路由应在后端再次验证
- 使用路由独享守卫处理特殊权限
-
开发体验:
- 为路由添加name属性方便跳转
- 使用路由元信息管理页面标题
- 实现进度条提升用户体验
六、综合实战:企业级路由方案
1. 完整路由配置示例
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import NProgress from 'nprogress'const routes = [{path: '/',name: 'home',component: () => import('@/views/HomeView.vue'),meta: {title: '首页',requiresAuth: false,cache: true}},{path: '/login',name: 'login',component: () => import('@/views/LoginView.vue'),meta: {title: '登录',guestOnly: true}},{path: '/dashboard',name: 'dashboard',component: () => import('@/views/DashboardView.vue'),meta: {title: '仪表盘',requiresAuth: true}},{path: '/admin',name: 'admin',component: () => import('@/views/layouts/AdminLayout.vue'),meta: {title: '管理后台',requiresAuth: true,roles: ['admin']},children: [{path: '',name: 'admin-dashboard',component: () => import('@/views/admin/DashboardView.vue'),meta: { title: '控制台' }},{path: 'users',name: 'admin-users',component: () => import('@/views/admin/UsersView.vue'),meta: { title: '用户管理' }}]},{path: '/:pathMatch(.*)*',name: 'not-found',component: () => import('@/views/NotFoundView.vue'),meta: {title: '页面不存在'}}
]const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes,scrollBehavior(to, from, savedPosition) {if (savedPosition) return savedPositionif (to.hash) return { el: to.hash, behavior: 'smooth' }return { top: 0 }}
})// 进度条配置
NProgress.configure({ showSpinner: false })// 全局前置守卫
router.beforeEach(async (to, from, next) => {NProgress.start()const authStore = useAuthStore()const isAuthenticated = authStore.isAuthenticatedconst userRole = authStore.user?.role || 'guest'// 设置页面标题document.title = to.meta.title ? `${to.meta.title} | 我的应用` : '我的应用'// 检查认证if (to.meta.requiresAuth && !isAuthenticated) {return next({name: 'login',query: { redirect: to.fullPath }})}// 检查角色权限if (to.meta.roles && !to.meta.roles.includes(userRole)) {return next({ name: 'forbidden' })}// 已登录用户访问guestOnly路由if (to.meta.guestOnly && isAuthenticated) {return next({ name: 'home' })}next()
})// 全局后置钩子
router.afterEach(() => {NProgress.done()
})export default router
2. 路由工具函数
// utils/router.js
export function resetRouter() {const newRouter = createRouter()router.matcher = newRouter.matcher // 重置路由
}export function loadRoutesByRole(role) {const dynamicRoutes = []if (role === 'admin') {dynamicRoutes.push({path: '/admin',component: () => import('@/views/AdminView.vue'),children: [// 管理员专属路由]})}dynamicRoutes.forEach(route => {router.addRoute(route)})
}export function getRouteTitle(route) {return route.meta.title || ''
}
3. 路由与状态管理集成
// stores/app.js
import { defineStore } from 'pinia'
import { useRouter } from 'vue-router'export const useAppStore = defineStore('app', {state: () => ({cachedViews: [],visitedViews: []}),actions: {addCachedView(view) {if (this.cachedViews.includes(view.name)) returnif (view.meta?.cache) {this.cachedViews.push(view.name)}},addVisitedView(view) {const existing = this.visitedViews.find(v => v.path === view.path)if (existing) {if (existing.fullPath !== view.fullPath) {// 更新现有记录Object.assign(existing, view)}return}this.visitedViews.push(Object.assign({}, view, {title: view.meta?.title || '未知'}))},async logout() {const router = useRouter()// 清理状态this.$reset()// 重定向到登录页await router.push('/login')}}
})
通过本指南,您已经全面掌握了Vue Router的核心概念和高级用法。从基础配置到导航守卫,从动态路由到状态集成,这些知识将帮助您构建复杂且高效的单页应用程序。实际项目中应根据具体需求选择合适的路由方案,并遵循最佳实践以确保应用的性能和可维护性。