在前端单页应用(SPA)中,路由系统是实现页面切换的核心。而导航守卫,就是路由系统的 "守门人",它能在路由跳转的各个阶段介入,实现权限控制、页面拦截、数据预加载等关键功能。本文将以 Vue Router 为例,全面拆解导航守卫的原理、分类和实战场景。
一、导航守卫的核心作用
单页应用的路由跳转是客户端行为,不会触发浏览器的页面刷新。导航守卫的本质,是在路由跳转的生命周期中植入钩子函数,让开发者可以:
- 权限校验:比如未登录用户拦截到登录页、普通用户禁止访问管理员路由。
- 数据预加载:进入页面之前请求数据,避免页面渲染后出现空白。
- 页面拦截:比如表单未提交时,阻止用户跳转并提示保存。
- 路由跳转日志:记录用户的页面访问轨迹,用于埋点分析。
二、导航守卫的分类与使用
Vue Router 中的导航守卫分为 3 大类,覆盖了路由跳转的完整流程:
- 全局守卫
- 路由独享守卫
- 组件内守卫
2.1 全局守卫
全局守卫会作用于所有路由 ,通常在路由实例初始化时定义,分为 beforeEach、beforeResolve、afterEach 三个钩子。
(1)router.beforeEach:全局前置守卫
这是最常用的全局守卫,在路由跳转前 触发,适合做全局权限控制。
语法:
javascript
运行
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// to: 即将要进入的目标路由对象
// from: 当前正要离开的路由对象
// next: 必须调用该方法,才能进入下一个钩子
next() // 放行
// next(false) // 中断导航
// next('/login') // 跳转到指定路由
// next({ path: '/login', replace: true }) // 跳转且不留下历史记录
})
实战场景:登录校验
javascript
运行
router.beforeEach((to, from, next) => {
// 判断目标路由是否需要登录权限
if (to.meta.requiresAuth) {
const token = localStorage.getItem('token')
if (token) {
next() // 已登录,放行
} else {
next('/login') // 未登录,跳转到登录页
}
} else {
next() // 不需要权限,直接放行
}
})
// 路由配置中添加 meta 字段标记权限
const routes = [
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true } // 需要登录
},
{ path: '/login', component: Login }
]
(2)router.beforeResolve:全局解析守卫
在所有组件内守卫和异步路由组件解析之后 触发,与 beforeEach 类似,但它会等待所有异步操作完成后再执行。
(3)router.afterEach:全局后置钩子
在路由跳转完成后 触发,它没有 next 函数,因为此时导航已经完成,只能用于页面埋点、滚动条重置等操作。
javascript
运行
router.afterEach((to, from) => {
// 记录页面访问日志
console.log(`用户从 ${from.path} 跳转到 ${to.path}`)
// 重置页面滚动条到顶部
window.scrollTo(0, 0)
})
2.2 路由独享守卫
路由独享守卫只作用于单个路由 ,在路由配置中通过 beforeEnter 定义,适合对特定路由进行拦截。
语法与实战:
javascript
运行
const routes = [
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true },
beforeEnter: (to, from, next) => {
// 仅对 /admin 路由生效的权限校验
const isAdmin = localStorage.getItem('role') === 'admin'
if (isAdmin) {
next()
} else {
next('/403') // 无权限跳转到 403 页面
}
}
}
]
2.3 组件内守卫
组件内守卫是定义在组件内部 的钩子,用于监听组件自身的路由跳转,分为 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。
(1)beforeRouteEnter:进入组件前触发
注意 :此时组件实例还未创建(this 为 undefined),若要访问组件实例,需通过 next 的回调函数。
javascript
运行
export default {
beforeRouteEnter (to, from, next) {
// 通过 next 回调访问组件实例
next(vm => {
// vm 就是组件实例
vm.fetchData() // 进入组件前预加载数据
})
}
}
(2)beforeRouteUpdate:路由参数更新时触发
当路由参数变化但组件被复用(比如 /user/:id 从 /user/1 跳转到 /user/2)时触发,可用于更新组件数据。
javascript
运行
export default {
beforeRouteUpdate (to, from, next) {
// 更新路由参数对应的用户信息
this.userId = to.params.id
this.fetchUserInfo()
next()
}
}
(3)beforeRouteLeave:离开组件时触发
适合做页面离开拦截,比如表单未提交时提示用户。
javascript
运行
export default {
data () {
return {
formData: {},
isDirty: false // 标记表单是否修改
}
},
beforeRouteLeave (to, from, next) {
if (this.isDirty) {
const confirm = window.confirm('表单未保存,确定离开吗?')
if (confirm) {
next()
} else {
next(false) // 取消导航
}
} else {
next()
}
}
}
三、导航守卫的执行顺序
当一个路由跳转触发时,各类守卫的执行顺序是固定的,遵循 "前置 → 解析 → 组件内 → 后置" 的流程:
- 全局前置守卫
beforeEach - 路由独享守卫
beforeEnter - 组件内守卫
beforeRouteEnter - 全局解析守卫
beforeResolve - 导航完成
- 全局后置钩子
afterEach - 组件内守卫
beforeRouteEnter的next回调
四、常见问题与注意事项
4.1 不要忘记调用 next
在 beforeEach、beforeEnter 等带 next 的守卫中,必须调用 next() 才能继续导航,否则会导致路由跳转卡住。
4.2 避免无限循环
如果在守卫中跳转到自身路由,会触发无限循环,比如:
javascript
运行
// 错误示例:会无限触发 beforeEach
router.beforeEach((to, from, next) => {
if (to.path !== '/login') {
next('/login') // 若 /login 也触发该守卫,会无限循环
}
})
解决方法:添加条件判断,避免重复跳转。
4.3 异步操作的处理
如果在守卫中执行异步操作(比如请求接口校验权限),需要在异步回调中调用 next:
javascript
运行
router.beforeEach((to, from, next) => {
// 异步校验权限
checkPermission().then(res => {
if (res) next()
else next('/403')
})
})
五、总结
导航守卫是前端路由系统的核心能力,它让开发者能够精准掌控路由跳转的每一个环节。无论是 Vue Router 还是 React Router(通过 useNavigate、useLocation 等钩子实现类似功能),导航守卫的设计思想都是一致的 ------在生命周期中植入钩子,实现业务逻辑的灵活介入。
掌握导航守卫的分类和执行顺序,就能轻松应对权限控制、数据预加载等常见需求,让单页应用的路由跳转更安全、更智能。
👉 **觉得有用的点点关注谢谢~**