在前端SPA(单页应用)开发中,路由是连接不同页面视图的核心枢纽。而路由守卫则是守护路由跳转安全、控制跳转流程的“门卫”,它能在路由跳转的关键节点插入自定义逻辑,实现权限控制、页面拦截、数据预加载等核心需求。Vue Router(Vue生态)、React Router(React生态)等主流路由库均内置了路由守卫机制,核心设计思路一致。本文将以Vue Router为例,详细拆解全局守卫、路由独享守卫、组件守卫的分类、用法、执行顺序及实际应用场景。
一、路由守卫的核心概念与作用
路由守卫本质是“钩子函数”,在路由生命周期的特定阶段被触发。路由的完整生命周期可简化为:导航触发 → 验证前置条件 → 跳转中 → 跳转完成 → 离开当前路由。
路由守卫的核心作用包括:
权限控制:未登录用户拦截跳转到登录页、角色权限校验(如普通用户禁止访问管理员页面);
数据预加载:进入页面前提前请求核心数据,避免页面渲染后出现空白;
页面拦截:禁止未保存表单的用户离开当前页面(如编辑页面未提交时提示);
全局行为控制:如路由跳转时显示加载动画、统计页面访问量等。
二、全局守卫:作用于所有路由
全局守卫注册在路由实例上,对所有路由的跳转都生效,适合实现全局层面的控制(如登录校验、全局加载动画)。Vue Router提供3种全局守卫:
2.1 全局前置守卫(beforeEach)
最常用的全局守卫,在路由跳转前触发(导航即将开始时),可用于拦截未登录用户、判断权限等。
用法:
// 引入路由实例importrouterfrom'./router'// 全局前置守卫router.beforeEach((to,from,next)=>{// to:即将进入的目标路由对象(包含路径、参数等信息)// from:即将离开的当前路由对象// next:必须调用的函数,控制导航流程// next():放行,进入to路由// next('/login'):强制跳转到login路由// next(false):取消导航,留在from路由// next(error):传递错误,触发router.onError()// 示例:未登录用户拦截(排除登录页)constisLogin=localStorage.getItem('token')// 假设token存本地if(to.path!=='/login'&&!isLogin){next('/login')// 未登录且非登录页,跳登录页}else{next()// 已登录或访问登录页,放行}})关键说明:
next函数是核心,必须调用否则导航会“卡死”;支持传递路径、路由对象或false,灵活控制跳转逻辑。
2.2 全局解析守卫(beforeResolve)
在路由跳转前触发,但时机晚于beforeEach,且在“所有组件内前置守卫和路由独享守卫执行完成后”触发。适合需要等待所有前置逻辑完成后再做的操作(如数据预加载完成后校验)。
用法:
router.beforeResolve((to,from,next)=>{// 逻辑与beforeEach类似,但执行时机更靠后next()})2.3 全局后置钩子(afterEach)
在路由跳转完成后触发(导航已确认,组件已渲染),无next函数,无法拦截导航。适合实现跳转后的全局行为(如关闭加载动画、统计访问量)。
用法:
router.afterEach((to,from)=>{// 示例1:关闭全局加载动画window.loading=false// 示例2:统计页面访问量console.log(`访问页面:${to.path}`)// 可通过to.meta获取路由元信息(如下文的title)document.title=to.meta.title||'默认标题'})三、路由独享守卫:作用于单个路由
路由独享守卫注册在路由规则中,仅对当前路由生效,适合针对单个路由的特殊控制(如管理员页面单独的权限校验)。Vue Router中只有1种路由独享守卫:beforeEnter。
3.1 用法:
constroutes=[{path:'/admin',component:()=>import('./views/Admin'),// 路由独享守卫:仅对/admin路由生效beforeEnter:(to,from,next)=>{// 示例:校验是否为管理员角色constrole=localStorage.getItem('role')if(role==='admin'){next()// 管理员放行}else{next('/403')// 非管理员跳403页面}}}]3.2 注意点:
beforeEnter仅在“进入该路由时”触发,离开时不触发;
执行顺序:全局前置守卫(beforeEach)→ 路由独享守卫(beforeEnter)→ 组件内前置守卫(beforeRouteEnter)。
四、组件守卫:作用于当前组件
组件守卫定义在Vue组件内部,仅对当前组件对应的路由生效,适合实现组件层面的个性化控制(如表单未保存提示、组件内数据预加载)。Vue Router提供3种组件守卫:
4.1 组件内前置守卫(beforeRouteEnter)
在进入组件对应的路由前触发,此时组件实例尚未创建(this为undefined),无法访问组件内的数据和方法。
用法:
<script> export default { name: 'EditPage', beforeRouteEnter(to, from, next) { // 此时this === undefined,无法访问组件实例 // 若需预加载数据,可在next的回调中执行(回调在组件创建后触发) next(vm => { // vm为组件实例,可访问this的所有属性和方法 vm.fetchData(to.params.id) // 预加载当前编辑项的数据 }) } } </script>4.2 组件内更新守卫(beforeRouteUpdate)
当“路由参数变化但组件未重新创建”时触发(如从/user/1跳转到/user/2,同一User组件复用)。适合监听路由参数变化并更新数据。
用法:
<script> export default { name: 'UserPage', beforeRouteUpdate(to, from, next) { // 此时组件已创建,this可用 // 示例:路由参数id变化,重新请求用户数据 this.userId = to.params.id this.fetchUserInfo() next() } } </script>4.3 组件内后置守卫(beforeRouteLeave)
在离开当前组件对应的路由前触发,此时组件实例仍存在(this可用),适合做离开前的校验(如表单未保存提示)。
用法:
<script> export default { name: 'FormPage', data() { return { formEdited: false // 标记表单是否已编辑未提交 } }, beforeRouteLeave(to, from, next) { if (this.formEdited) { // 弹出确认框,询问用户是否离开 if (confirm('表单未保存,确定离开吗?')) { next() // 确认离开,放行 } else { next(false) // 取消离开,留在当前页面 } } else { next() // 表单未编辑,直接放行 } } } </script>五、路由守卫的执行顺序(关键)
当触发路由跳转时,各类守卫的执行顺序如下(以“从A组件跳转到B组件”为例):
全局前置守卫(beforeEach);
目标路由的路由独享守卫(beforeEnter);
目标组件的组件内前置守卫(beforeRouteEnter);
全局解析守卫(beforeResolve);
导航确认,组件渲染;
全局后置钩子(afterEach);
若离开的组件有beforeRouteLeave,其执行在第1步之前。
记忆口诀:离开守卫先执行 → 全局前置 → 路由独享 → 组件前置 → 全局解析 → 导航完成 → 全局后置。
六、实际应用场景总结
| 需求场景 | 推荐使用的守卫 | 核心逻辑 |
|---|---|---|
| 未登录用户拦截 | 全局前置守卫(beforeEach) | 判断本地是否有token,无则跳登录页 |
| 管理员页面权限校验 | 路由独享守卫(beforeEnter) | 单独校验角色是否为admin,否则跳403 |
| 表单未保存提示 | 组件内后置守卫(beforeRouteLeave) | 监听表单编辑状态,离开前弹出确认框 |
| 页面跳转加载动画 | 全局前置+后置守卫 | beforeEach显示动画,afterEach关闭动画 |
| 组件数据预加载 | 组件内前置守卫(beforeRouteEnter) | 在next回调中调用接口,提前获取数据 |
| 路由参数变化更新数据 | 组件内更新守卫(beforeRouteUpdate) | 监听to.params变化,重新请求数据 |
七、常见问题与注意事项
next函数必须调用:全局前置守卫和路由独享守卫中,若不调用next(),导航会一直处于“等待中”状态,导致页面卡死;
beforeRouteEnter中this为undefined:若需访问组件实例,必须通过next的回调函数(vm参数);
避免重复拦截:全局守卫已做登录校验的,无需在组件内重复做,避免逻辑冗余;
异步逻辑处理:若守卫中有异步操作(如请求接口校验权限),需在异步完成后再调用next(),示例:
router.beforeEach(async (to, from, next) => { const res = await axios.get('/api/checkPermission') // 异步校验权限 if (res.data.allow) { next() } else { next('/403') } })
八、总结
路由守卫是前端路由的核心能力,核心价值在于“控制路由跳转流程”。全局守卫管全局、路由独享守卫管单个路由、组件守卫管组件内细节,三者相互补充。使用时需牢记执行顺序,根据需求场景选择合适的守卫类型,同时注意next函数的调用时机和异步逻辑的处理,避免出现导航卡死等问题。
无论是Vue Router还是React Router,路由守卫的设计思路一致,掌握其核心逻辑后,可轻松应对各类权限控制、数据预加载等场景,提升应用的安全性和用户体验。