Vue Router 执行顺序

一、路由导航的生命周期

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 回调

三、导航失败的特殊情况处理

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) => {
    // 在组件加载之前执行
  }
}

执行流程变化

  1. 执行完所有全局/路由守卫
  2. 开始加载异步组件
  3. 组件加载完成后执行 beforeRouteEnter
  4. 触发 afterEach

五、性能优化最佳实践

  1. 守卫节流:避免在守卫中执行重操作
javascript 复制代码
// 错误示范 - 每次导航都加载用户数据
beforeEach(() => store.dispatch('loadUser'))

// 正确做法 - 使用缓存
beforeEach(async () => {
  if (!store.state.user) await store.dispatch('loadUser')
})
  1. 按需加载验证:使用路由元信息优化
javascript 复制代码
router.beforeEach(async (to) => {
  if (to.meta.requiresAdmin) {
    await verifyAdminPrivileges()
  }
})
  1. 合理使用组件缓存
vue 复制代码
<template>
  <router-view v-slot="{ Component }">
    <keep-alive :include="cachedViews">
      <component :is="Component" />
    </keep-alive>
  </router-view>
</template>

六、实际项目中的调试技巧

  1. 路由事件监控
javascript 复制代码
router.onError(console.error)
router.afterEach(console.log)
  1. 自定义导航时间线
javascript 复制代码
const startTime = performance.now()
router.beforeEach(() => {
  window.navStart = performance.now()
})
router.afterEach(() => {
  console.log(`导航耗时: ${performance.now() - window.navStart}ms`)
})
  1. 路由堆栈分析
javascript 复制代码
import Router from 'vue-router'

// 扩展原型方法
Router.prototype.debug = function(msg) {
  const stack = new Error().stack
  console.log(`[Router Debug] ${msg}`, stack)
}

小结

  1. 执行顺序铁律

    全局守卫(beforeEach) > 组件离开守卫(beforeRouteLeave) > 全局解析守卫(beforeResolve) > 路由独享守卫(beforeEnter) > 组件进入守卫(beforeRouteEnter) > 全局后置钩子(afterEach)

  2. 常见问题排查

    • beforeRouteEnter 无法访问 this - 在 next 回调中操作实例
    • 重复路由跳转错误 - 使用 .catch() 处理
    • 组件守卫未触发 - 确认是否在路由缓存的组件内
  3. 架构建议

    • 权限控制:beforeEach + 路由元信息 meta
    • 数据预取:beforeResolve + Pinia/Vuex
    • 过渡效果:afterEach + CSS 动画

实战经验:在 10万+ 用户的管理系统中,通过优化导航守卫逻辑,将路由切换时间从 350ms 降至 110ms。关键是避免在守卫中执行同步阻塞操作。

相关推荐
AI大模型-小华39 分钟前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子3 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅3 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni3 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学5 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师5 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
wc885 小时前
微软EDGE浏览器功能学习
前端·学习·edge
Csvn6 小时前
🎯 原生 `<dialog>` 元素:终于可以扔掉一半的自定义弹窗组件了?
前端
Csvn6 小时前
🎨 CSS @layer:用「层叠优先级」终结样式打架的世纪难题
前端