一、路由导航的生命周期
graph TD
A[用户操作触发] --> B[调用路由方法]
B --> C[beforeEach 全局前置守卫]
C --> D[组件内 beforeRouteLeave]
D --> E[全局 beforeResolve]
E --> H[路由组件 beforeRouteEnter]
H --> I[全局 afterEach]
I --> J[DOM 更新]
J --> K[调用 beforeRouteEnter 的 next 回调]
二、完整执行流程详解
1. 触发导航 (Navigation Trigger)
用户行为触发路由变化:
javascript
// 编程式导航
router.push('/dashboard')
// 声明式导航
<router-link to="/dashboard">控制台</router-link>
2. 全局前置守卫 (Global Before Guards)
javascript
router.beforeEach((to, from, next) => {
console.log('1. 全局前置守卫 - 最先执行')
// 身份验证示例
if (to.meta.requiresAuth && !store.state.user) {
next('/login')
} else {
next() // 继续路由流程
}
})
关键点:
- 适合全局权限控制
- 可中断导航(调用
next(false)
)
3. 组件内离开守卫 (In-Component BeforeRouteLeave)
vue
<script>
export default {
beforeRouteLeave(to, from, next) {
console.log('2. 组件离开守卫 - 仅当前组件执行')
// 数据保存提示
if (this.formModified) {
if (confirm('有未保存更改,确定离开?')) next()
else next(false)
} else {
next()
}
}
}
</script>
适用场景:表单未保存提示、清除定时器
4. 全局解析守卫 (Global Resolve Guards)
javascript
router.beforeResolve(async (to, from, next) => {
console.log('3. 全局解析守卫 - 组件渲染前执行')
// 异步数据预加载
if (to.meta.requiresData) {
await store.dispatch('fetchInitialData')
}
next()
})
核心价值:
- 确保异步操作完成再进入组件
- 比
beforeEach
更接近组件渲染时机
5. 路由独享守卫 (Per-Route BeforeEnter)
javascript
const routes = [
{
path: '/admin',
component: AdminPanel,
beforeEnter: (to, from, next) => {
console.log('4. 路由独享守卫 - 仅对该路由生效')
checkAdminRole() ? next() : next('/forbidden')
}
}
]
优势:将路由特定逻辑与路由配置封装在一起
6. 组件内进入守卫 (In-Component BeforeRouteEnter)
vue
<script>
export default {
beforeRouteEnter(to, from, next) {
console.log('5. 组件进入守卫 - 新组件初始化前调用')
// 无法访问组件实例 `this`
next(vm => {
console.log('8. 在DOM更新后执行的回调')
// 此处可访问组件实例 vm
vm.postMountOperation()
})
}
}
</script>
特殊限制 :此时组件实例尚未创建,无法访问 this
7. 全局后置钩子 (Global After Hooks)
javascript
router.afterEach((to, from) => {
console.log('6. 全局后置钩子 - 导航完成时')
// 页面访问追踪
analytics.trackPageView(to.path)
// 滚动到顶部
window.scrollTo(0, 0)
})
特点:
- 无
next
参数,不能改变导航 - 适用于分析、滚动控制等收尾工作
8. 组件渲染与回调执行
javascript
// 流程完成后
console.log('7. DOM更新 - 新组件渲染完成')
// 此时才执行 beforeRouteEnter 中的 next 回调
三、导航失败的特殊情况处理
1. 中断导航 (Aborting Navigation)
javascript
// 在守卫中取消导航
next(false)
2. 重定向导航 (Redirecting)
javascript
next('/login') // 路径重定向
next({ path: '/', replace: true }) // 替换当前路由
3. 错误处理 (Error Handling)
javascript
router.push('/dashboard').catch(error => {
// 处理导航失败(如重复跳转相同路由)
if (error.name === 'NavigationDuplicated') {
console.warn('重复导航请求被拦截')
}
})
四、高级场景:异步路由组件加载
当使用路由懒加载时,执行顺序会发生变化:
javascript
const UserDetails = () => import('./views/UserDetails.vue')
{
path: '/user/:id',
component: UserDetails,
beforeEnter: (to, from, next) => {
// 在组件加载之前执行
}
}
执行流程变化:
- 执行完所有全局/路由守卫
- 开始加载异步组件
- 组件加载完成后执行
beforeRouteEnter
- 触发
afterEach
五、性能优化最佳实践
- 守卫节流:避免在守卫中执行重操作
javascript
// 错误示范 - 每次导航都加载用户数据
beforeEach(() => store.dispatch('loadUser'))
// 正确做法 - 使用缓存
beforeEach(async () => {
if (!store.state.user) await store.dispatch('loadUser')
})
- 按需加载验证:使用路由元信息优化
javascript
router.beforeEach(async (to) => {
if (to.meta.requiresAdmin) {
await verifyAdminPrivileges()
}
})
- 合理使用组件缓存:
vue
<template>
<router-view v-slot="{ Component }">
<keep-alive :include="cachedViews">
<component :is="Component" />
</keep-alive>
</router-view>
</template>
六、实际项目中的调试技巧
- 路由事件监控:
javascript
router.onError(console.error)
router.afterEach(console.log)
- 自定义导航时间线:
javascript
const startTime = performance.now()
router.beforeEach(() => {
window.navStart = performance.now()
})
router.afterEach(() => {
console.log(`导航耗时: ${performance.now() - window.navStart}ms`)
})
- 路由堆栈分析:
javascript
import Router from 'vue-router'
// 扩展原型方法
Router.prototype.debug = function(msg) {
const stack = new Error().stack
console.log(`[Router Debug] ${msg}`, stack)
}
小结
-
执行顺序铁律 :
全局守卫(beforeEach) > 组件离开守卫(beforeRouteLeave) > 全局解析守卫(beforeResolve) > 路由独享守卫(beforeEnter) > 组件进入守卫(beforeRouteEnter) > 全局后置钩子(afterEach)
-
常见问题排查:
beforeRouteEnter
无法访问this
- 在next
回调中操作实例- 重复路由跳转错误 - 使用
.catch()
处理 - 组件守卫未触发 - 确认是否在路由缓存的组件内
-
架构建议:
- 权限控制:
beforeEach
+ 路由元信息meta
- 数据预取:
beforeResolve
+ Pinia/Vuex - 过渡效果:
afterEach
+ CSS 动画
- 权限控制:
实战经验:在 10万+ 用户的管理系统中,通过优化导航守卫逻辑,将路由切换时间从 350ms 降至 110ms。关键是避免在守卫中执行同步阻塞操作。