当前位置: 首页 > news >正文

3.4路由守卫

在Vue 2中,Vue Router提供了路由守卫(Navigation Guards)的功能,这允许你在路由跳转之前或之后执行逻辑。这些守卫可以用来处理认证、授权、页面标题修改、记录分析数据等任务

路由守卫类型

  • 全局守卫(Global Guards)

    • beforeEach:每次路由跳转前调用。它接受三个参数:to(目标路由对象)、from(当前路由对象)和next(进行导航确认的方法)。
    • beforeResolve:在路由被解析之后,导航被确认之前调用。它的使用方式与beforeEach相似。
    • afterEach:在路由跳转完成后调用。这个钩子不会接收到next函数,因为它不需要控制导航。

全局守卫:对所有路由生效的拦截逻辑

全局守卫注册后会作用于整个应用的所有路由跳转,包括前置守卫、解析守卫和后置守卫三种类型,其中前置守卫(beforeEach) 是实际开发中最常用的权限控制手段。

全局守卫的类型与触发时机

守卫类型触发时机参数说明
beforeEach路由跳转前触发(所有路由共用)to: 目标路由对象
from: 当前路由对象
next: 控制跳转的函数(Vue2中必填)
beforeResolve在所有组件内守卫和异步路由组件解析后触发(较少使用)同beforeEach
afterEach路由跳转完成后触发(无next参数,仅用于页面状态更新如滚动位置重置)to: 目标路由对象
from: 当前路由对象

典型应用:全局登录状态验证

router.beforeEach((to,  from, next) => { // 判断目标路由是否需要登录权限(通过路由元信息meta.requireAuth 标记) if (to.meta.requireAuth)  { const token = sessionStorage.getItem('token');  if (token) { next(); // 已登录,放行 } else { next('/login'); // 未登录,重定向至登录页 } } else { next(); // 无需登录的路由直接放行 } 
}); 

关键步骤

综合案例:

  1. 在路由配置中通过meta: { requireAuth: true }标记需要权限的路由2;
  2. 登录成功后将token存入sessionStorage(临时存储,关闭浏览器失效)或localStorage(持久化存储)1;
  3. 守卫中通过to.meta.requireAuth 判断是否拦截,并验证token有效性。
创建路由配置并添加全局守卫

创建 router/index.js 文件,并设置路由及所有全局守卫:

import Vue from 'vue';
import Router from 'vue-router';
import Login from '@/components/Login.vue';
import Dashboard from '@/components/Dashboard.vue';
import ErrorPage from '@/components/ErrorPage.vue';Vue.use(Router);// 假设的登录状态管理函数
function isAuthenticated() {return localStorage.getItem('authToken') !== null;
}const router = new Router({routes: [{path: '/login',name: 'Login',component: Login},{path: '/dashboard',name: 'Dashboard',component: Dashboard,meta: { requiresAuth: true } // 添加元信息,标识需要认证},{path: '/error',name: 'Error',component: ErrorPage},{path: '*',redirect: '/login'}]
});// beforeEach 守卫:用于验证用户是否已登录
router.beforeEach((to, from, next) => {if (to.matched.some(record => record.meta.requiresAuth)) {if (!isAuthenticated()) {next({path: '/login',query: { redirect: to.fullPath }});} else {next();}} else {next(); // 确保一定要调用 next()}
});// beforeResolve 守卫:模拟数据加载完成前的操作
router.beforeResolve((to, from, next) => {console.log("准备完成所有可能的异步组件和钩子...");// 这里可以执行一些异步操作,比如数据预取等next();
});// afterEach 守卫:用于更新页面标题
router.afterEach((to, from) => {document.title = to.name || '默认标题'; // 根据路由名称或者自定义逻辑设置页面标题
});export default router;
 创建组件

接下来,我们需要创建三个组件:Login.vueDashboard.vueErrorPage.vue

Login.vue
<template><div><h2>登录</h2><button @click="login">登录</button></div>
</template><script>
export default {methods: {login() {// 设置登录状态localStorage.setItem('authToken', 'some-auth-token');const redirectTo = this.$route.query.redirect || '/dashboard';this.$router.push(redirectTo);}}
}
</script>
Dashboard.vue
<template><div><h2>欢迎来到仪表盘</h2><p>这是受保护的页面。</p><button @click="logout">退出登录</button></div>
</template><script>
export default {methods: {logout() {localStorage.removeItem('authToken');this.$router.push('/login');}}
}
</script>
ErrorPage.vue
<template><div><h2>404 - 页面未找到</h2></div>
</template>
在主应用中使用路由

最后,在你的主应用文件中(例如 main.js),引入并使用路由器:

import Vue from 'vue';
import App from './App.vue';
import router from './router';new Vue({el: '#app',router,render: h => h(App)
});

通过这个例子,我们展示了如何在 Vue 2 应用程序中使用所有的全局守卫:beforeEachbeforeResolveafterEach。这使得我们可以基于用户的登录状态控制路由访问,处理异步数据加载,并动态地更新页面标题。

组件内守卫:组件级别的路由拦截

组件内守卫仅作用于当前组件对应的路由,定义在组件选项中,适用于组件专属逻辑(如表单未保存提示、动态数据加载)。

组件内守卫的三种类型

组件内守卫仅作用于当前组件对应的路由,定义在组件选项中,适用于组件专属逻辑(如表单未保存提示、动态数据加载)。

  • beforeRouteEnter:在渲染该组件的对应路由被确认前调用。你不能在这个守卫中访问this,因为当守卫执行时实例还未创建。路由进入前触发(此时组件实例未创建,无法通过this访问组件),需通过next(vm => { ... })回调获取组件实例:
    beforeRouteEnter(to, from, next) { next(vm => { // vm即当前组件实例,可调用组件方法或访问数据 vm.loadData(to.params.id);  }); 
    } 
    
  • beforeRouteUpdate:在当前路由改变,但是该路由被复用时调用。例如,对于一个带有动态参数的路径,在参数改变时,组件会被复用,此时可以使用此守卫来响应数据的变化。路由参数变化时触发(如从/user/1跳转到/user/2,组件复用场景),可直接访问this
    beforeRouteUpdate(to, from, next) { this.userId  = to.params.id;  // 更新组件数据 next(); 
    } 
    
  • beforeRouteLeave:在导航离开该组件的对应路由时调用。它可以用来阻止用户意外离开,避免未保存的数据丢失.路由离开前触发(常用于阻止未保存操作),通过next(false)阻止跳转:
    beforeRouteLeave(to, from, next) { if (this.formModified)  { if (confirm('表单未保存,确定离开吗?')) { next(); } else { next(false); // 取消跳转 } } else { next(); } 
    } 

综合案例:

<template><div><h2>编辑用户信息</h2><input v-model="form.name" placeholder="姓名" /><input v-model="form.email" placeholder="邮箱" /><button @click="save">保存</button><button @click="$router.go(-1)">返回</button></div>
</template><script>
export default {name: 'UserEdit',data() {return {form: {name: '',email: ''},originalForm: {}, // 保存原始数据用于比较hasUnsavedChanges: false}},// 👇 组件内守卫:进入路由前beforeRouteEnter(to, from, next) {// 注意:此时组件实例还未创建,不能访问 `this`// 如果需要获取数据,建议通过 API 获取,并通过 `next` 传递给组件console.log('准备进入用户编辑页,来源路由:', from.path)// 模拟异步获取用户数据fetchUserById(to.params.id).then(userData => {next(vm => {// `vm` 是组件实例vm.form = { ...userData }vm.originalForm = { ...userData }})}).catch(() => {next(false) // 取消导航// 或者跳转到错误页// next('/error')})},// 👇 组件内守卫:路由更新时(例如 id 变化)beforeRouteUpdate(to, from, next) {console.log('路由参数更新,准备加载新用户数据')// 可以在此处重新获取数据if (this.hasUnsavedChanges) {const answer = window.confirm('你有未保存的更改,确定要切换用户吗?')if (!answer) {next(false)return}}// 获取新用户数据fetchUserById(to.params.id).then(userData => {this.form = { ...userData }this.originalForm = { ...userData }this.hasUnsavedChanges = falsenext()}).catch(() => {next(false)})},// 👇 组件内守卫:离开当前路由时beforeRouteLeave(to, from, next) {console.log('准备离开编辑页,目标路由:', to.path)// 判断是否有未保存的更改if (this.hasUnsavedChanges) {const answer = window.confirm('你的更改尚未保存,确定要离开吗?')if (answer) {next() // 允许离开} else {next(false) // 取消导航}} else {next() // 直接允许离开}},watch: {form: {handler() {// 监听表单变化,标记为有未保存更改this.hasUnsavedChanges = true},deep: true}},methods: {save() {// 模拟保存console.log('保存数据:', this.form)this.hasUnsavedChanges = falsealert('保存成功!')}}
}// 模拟 API 请求
function fetchUserById(id) {return new Promise(resolve => {setTimeout(() => {resolve({id,name: `用户${id}`,email: `user${id}@example.com`})}, 500)})
}
</script>

测试场景

  1. 进入 /user/1/edit → 触发 beforeRouteEnter,加载用户1数据。
  2. 修改表单但不保存 → 点击返回或跳转其他页面 → 弹出确认框。
  3. 点击“确定” → 允许跳转;点击“取消” → 停留在当前页。
  4. 在页面内跳转到 /user/2/edit → 触发 beforeRouteUpdate,提示是否切换。

独享守卫:路由配置中的独立拦截逻辑

beforeEnter:在路由跳转进入该路由之前调用。它定义于路由配置内,并且只影响该特定路由。

独享守卫通过在路由配置中定义beforeEnter属性实现,仅对当前路由生效,优先级介于全局守卫和组件内守卫之间,适用于单一路由的特殊验证规则(如管理员权限校验)。

基础用法与执行顺序

  • 定义方式:在路由规则中添加beforeEnter函数:
    const routes = [ { path: '/admin', component: Admin, beforeEnter: (to, from, next) => { const role = sessionStorage.getItem('role');  if (role === 'admin') { next(); } else { next('/403'); // 非管理员跳转至无权限页 } } } 
    ]; 
    
  • 执行顺序:全局beforeEach → 独享beforeEnter → 组件内beforeRouteEnter

综合案例:

假设一个场景:只有管理员(userRole === 'admin')才能访问 /dashboard 路由,普通用户访问时跳转到 /login 并提示无权限

import Vue from 'vue' 
import Router from 'vue-router' 
import Dashboard from '@/views/Dashboard' 
import Login from '@/views/Login' 
import Home from '@/views/Home' Vue.use(Router)  export default new Router({ routes: [ { path: '/', name: 'Home', component: Home }, { path: '/login', name: 'Login', component: Login }, { path: '/dashboard', name: 'Dashboard', component: Dashboard, // 独享守卫:仅对当前路由生效 beforeEnter: (to, from, next) => { // 模拟用户角色(实际项目中可能从 Vuex/本地存储获取) const userRole = localStorage.getItem('userRole')  || 'user' // 默认普通用户 // 权限判断:只有 admin 可进入 if (userRole === 'admin') { next() // 允许进入当前路由 } else { alert('无权限访问!请先登录管理员账号') next('/login') // 普通用户强制跳转登录页 } } } ] 
}) 
<template> <div> <h2>登录页</h2> <button @click="login('user')">普通用户登录</button> <button @click="login('admin')">管理员登录</button> </div> 
</template> <script> 
export default { methods: { login(role) { localStorage.setItem('userRole',  role) // 存储用户角色 this.$router.push('/')  // 登录后跳转首页 } } 
} 
</script> 
测试效果
  • 普通用户登录后,点击跳转到 /dashboard,会触发 beforeEnter 守卫,提示“无权限”并强制跳转 /login
  • 管理员登录后,点击跳转到 /dashboard,守卫验证通过,正常进入页面。
http://www.dtcms.com/a/324835.html

相关文章:

  • Words or Vision Do Vision-Language Models Have Blind Faith in Text
  • Java中new的相关知识
  • nginx-主配置文件
  • Redis的批处理优化
  • 【高等数学】第八章 向量代数与空间解析几何——第六节 空间曲线及其方程
  • ECP HRFORM 提示ADS服务异常
  • 【嵌入式电机控制#补充3】SDK电机控制台的功能
  • C9800在NAT设备之后怎么办?
  • [创业之路-541]:经营分析会 - 企业的经营分析会,研发负责人负责提供哪些信息?
  • Linux810 shell 条件判断 文件工具 ifelse
  • 【牛客刷题】小红的项链(字节跳动面试题)
  • Linux操作系统从入门到实战(十七)进程与进程基本概念
  • doubletrouble靶机攻略
  • Docker 数据卷的核心原理与管理逻辑
  • 【数据结构与算法-Day 14】先进先出的公平:深入解析队列(Queue)的核心原理与数组实现
  • 端口扫描器用户使用手册 (EXE版)
  • JavaScript 变量:数据存储的核心机制
  • C++ 黑马 内存分配模型
  • 详解Windows(十六)——计划任务
  • Linux安装Jenkins-2.432,jdk17
  • Day11 原理篇
  • 华为防火墙配置指南【附实战案例】
  • python urllib模块怎么使用
  • 【软件测试】概念篇 — 详解
  • 广东省省考备考(第七十二天8.10)——言语理解与表达、判断推理(强化训练)
  • APISIX 路由优先级
  • SupChains团队:化学品制造商 ChampionX 供应链需求预测案例分享(十七)
  • 托福阅读记录
  • TypeScript 中的as const是什么?
  • 基于Actor-Critic策略的Atari中的pong_v3