Vue路由守卫全面指南:从全局到组件的控制艺术

225 阅读4分钟

Vue路由守卫全面指南:从全局到组件的控制艺术

引言

在Vue应用开发中,路由守卫是控制导航流程的关键机制。它们如同交通警察,精确管理着用户在应用中的每一次页面跳转。本文将系统介绍Vue Router提供的三类守卫钩子,帮助开发者实现细粒度的路由控制。

全局守卫:应用级别的导航管控

1. beforeEach - 全局前置守卫

router.beforeEach((to, from, next) => {
  // 设置页面标题
  document.title = to.meta.title || '默认标题'
  
  // 登录状态检查
  if (to.meta.requiresAuth && !localStorage.getItem('token')) {
    next('/login') // 跳转到登录页
  } else {
    next() // 放行
  }
})

参数解析

  • to:即将进入的目标路由对象
  • from:当前导航正要离开的路由
  • next:必须调用来resolve这个钩子的函数

典型应用场景

  • 身份验证
  • 页面标题设置
  • 全局权限检查

2. beforeResolve - 全局解析守卫

router.beforeResolve((to, from, next) => {
  // 确保异步组件已加载
  if (to.meta.requiresAsyncData) {
    fetchAsyncData().then(() => next())
  } else {
    next()
  }
})

特点

  • 在导航被确认前调用
  • 适合处理需要等待的数据加载

3. afterEach - 全局后置钩子

router.afterEach((to, from) => {
  // 页面访问统计
  trackPageView(to.fullPath)
})

注意事项

  • 没有next参数
  • 不影响导航本身
  • 适合日志记录、页面分析等

独享守卫:路由专属的守门人

const routes = [
  {
    path: '/admin',
    component: AdminPanel,
    beforeEnter: (to, from, next) => {
      if (user.role !== 'admin') {
        next('/forbidden')
      } else {
        next()
      }
    }
  }
]

优势

  • 只对特定路由生效
  • 配置直观,与路由定义在一起
  • 适合路由级别的特殊校验

这两种我一般习惯放在router文件夹下的index.js文件里,你要是想放到main.js文件里也行,整个文件代码示例为:

import { createRouter, createWebHistory } from 'vue-router'

const routes = [
    {
        path: '/',
        name: 'Home',
        component: () => import('../views/Home.vue'),
        meta: { title: '商城首页' }
    },
    {
        path: '/about',
        name: 'About',
        component: () => import('../views/About.vue'),
        meta: { title: '关于我们' },
        // beforeEnter: (to, from, next) => { // 单独的路由守卫
        //     console.log( to, from)
        //     next()
        // }
    },
]

const router = createRouter({
    history: createWebHistory(),
    routes
})

// 全局的前置钩子,这些钩子也可以写到main.js
// router.beforeEach((to, from, next) => {
//     document.title = to.meta.title
//     if (to.path !== '/') {
//         const isLogin = localStorage.getItem('isLogin')
//         if (isLogin) {
//             next()
//         } else {
//             // router.push('/')
//             alert('你没登陆,不允许跳转页面')
//             return
//         }
//     } else {
//         next()
//     }
// })

// router.beforeResolve(( to, from, next ) => {
//     console.log( to, from )
// })

// router.afterEach(( to, from ) => { 
//     console.log( to, from )
// })

export default router

下面这个组件守卫一般就是写到组件内的了

组件守卫:组件内部的控制逻辑

1. onBeforeRouteLeave - 离开守卫

这是简单的示例:

<template>
    <div>
        about Page
        <Hello />
    </div>
</template>

<script setup>
import Hello from "../components/Hello.vue";
import { onBeforeRouteLeave, onBeforeRouteUpdate} from 'vue-router'

onBeforeRouteLeave(( to, from, next) => {
    console.log('onBeforeRouterEnter')
    const flag = window.confirm('确定离开吗?')
    if(flag) {
        next()
    }
})
</script>

<style lang="css" scoped>

</style>

典型用途

  • 防止用户丢失未保存数据
  • 离开前的二次确认

2. onBeforeRouteUpdate - 更新守卫

<script setup>
import { onBeforeRouteUpdate } from 'vue-router'

onBeforeRouteUpdate(async (to, from, next) => {
  // 当路由参数变化时重新获取数据
  await fetchData(to.params.id)
  next()
})
</script>

适用场景

  • 相同组件但参数变化的场景
  • 动态路由参数更新时的数据处理

守卫执行顺序详解

  1. 导航触发:开始一次新的导航
  2. 失活组件:调用离开守卫(onBeforeRouteLeave)
  3. 全局前置:调用beforeEach
  4. 重用组件:调用beforeRouteUpdate
  5. 路由配置:调用beforeEnter
  6. 解析异步:调用beforeResolve
  7. 导航确认:完成导航
  8. 全局后置:调用afterEach
  9. DOM更新:组件渲染完成

实际开发建议

  1. 权限控制:在全局前置守卫实现统一的登录校验
  2. 数据预取:利用beforeResolve确保关键数据加载完成
  3. 用户提示:在组件离开守卫中添加未保存更改提示
  4. 性能优化:避免在守卫中进行复杂同步操作
  5. 错误处理:始终确保调用next(),避免阻塞导航
// 良好的错误处理示例
router.beforeEach((to, from, next) => {
  try {
    // 验证逻辑...
    if (valid) {
      next()
    } else {
      next('/login')
    }
  } catch (err) {
    next('/error') // 提供兜底方案
  }
})

总结

Vue Router的守卫系统提供了从全局到组件各个级别的导航控制能力。理解不同守卫的执行时机和适用场景,能够帮助开发者构建更安全、用户体验更好的单页应用。记住,守卫的核心价值在于它们让开发者能够以声明式的方式控制导航流程,而不是直接操作DOM或手动阻止默认行为。这些算是我的学习一个知识总结,有问题的可以来讨论。