likes
comments
collection
share

来看看路由守卫

作者站长头像
站长
· 阅读数 23

引言

大家好!今天我们要聊的是Vue 3中的一个非常重要的特性——路由守卫。如果你正在开发一个中大型的Vue应用,那么路由守卫是你绕不开的一个话题。无论是控制页面跳转的逻辑、执行一些数据预加载,还是对用户的权限进行验证,路由守卫都能帮助你更好地管理应用的状态和用户体验。

在我们深入探讨之前,先来快速总结一下为什么路由守卫如此重要:

  • 控制导航:通过路由守卫,我们可以控制用户在不同页面间的导航行为。
  • 数据预取:在进入某个页面前,可以提前获取数据并准备好状态。
  • 权限验证:在用户访问某些受保护的资源前进行身份验证。

假设我们正在构建一个博客系统,用户可以注册、登录,并且只有登录的用户才能查看和编辑他们的个人资料。此外,只有管理员才能访问管理面板。接下来,让我们通过这样一个常见的情况来深入了解路由守卫的各种用法吧。

  1. 登录页面 (/login) - 用户登录。
  2. 首页 (/) - 展示最新文章。
  3. 用户个人资料 (/profile) - 查看和编辑个人资料。
  4. 管理面板 (/admin) - 只有管理员可以访问。

一、认识路由守卫

首先,我们需要一个基本的Vue项目,并安装Vue Router。假设已经有了一个Vue 3项目,现在我们来添加路由功能。

npm install vue-router@next

接着,在项目的src目录下创建一个名为router的文件夹,并在其中新建index.js文件。我们将在这里设置基本的路由配置:

import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
    {
        path: '/',
        name: 'Home',
        component: Home
    },
    {
        path: '/profile',
        name: 'Profile',
        component: () => import('../views/Profile.vue'),
        meta: {
            requiresAuth: true
        }
    },
    {
        path: '/admin',
        name: 'AdminPanel',
        component: () => import('../views/AdminPanel.vue'),
        meta: {
            requiresAdmin: true
        }
    },
    {
        path: '/login',
        name: 'Login',
        component: () => import('../views/Login.vue')
    }
]

在上面,我们定义了两个简单的路由,主页和关于我们页面,步骤大家应该都轻车熟路了。注意懒加载以及meta字段,利用 meta 来判断是否需要登录来访问这个页面。利用接下来,就让我们添加一些路由守卫。


二、全局路由守卫

全局路由守卫是一种在路由改变之前运行的函数。我们可以利用这些守卫来做一些检查或者处理,比如来检查用户的登录状态以及是否有足够的权限访问某些页面。

1. 设计仓库

搞清楚不同身份的不同权限,设计出所需要的变量以及方法来控制权限的不同。

import { createStore } from 'vuex'

const store = createStore({
  state: () => ({
    user: null, // 存储当前用户的信息
    isLoggedIn: false, // 用户是否登录
    isAdmin: false, // 用户是否是管理员
  }),

  getters: {
    isLoggedIn: (state) => state.isLoggedIn,
    isAdmin: (state) => state.isAdmin,
    currentUser: (state) => state.user,
  },

  mutations: {
    SET_USER(state, user) {
      state.user = user
      state.isLoggedIn = !!user
      state.isAdmin = !!user && user.role === 'admin'
    },
    LOGOUT(state) {
      state.user = null
      state.isLoggedIn = false
      state.isAdmin = false
    },
  },

  actions: {
    login({ commit }, user) {
      // 模拟登录逻辑
      const success = true; // 假设登录总是成功
      if (success) {
        commit('SET_USER', user)
      }
    },
    logout({ commit }) {
      commit('LOGOUT')
    },
  },
})

export default store
State
  • user: 用于存储当前用户的详细信息,如用户名、角色等。
  • isLoggedIn: 表示用户是否已登录。
  • isAdmin: 表示用户是否是管理员。
Getters
  • isLoggedIn: 返回 isLoggedIn 的状态。
  • isAdmin: 返回 isAdmin 的状态。
  • currentUser: 返回当前用户的详细信息。
Mutations
  • SET_USER: 用于设置或清除用户信息,并更新 isLoggedIn 和 isAdmin 的状态。
  • LOGOUT: 用于清除用户信息,并将 isLoggedIn 和 isAdmin 设置为 false
Actions
  • login: 模拟登录逻辑,通过提交 mutation 更新用户状态。
  • logout: 提交 mutation 来清除用户信息。

2. 全局前置守卫

全局前置守卫会在每次路由切换前触发。我们可以用它来判断用户是否已经登录,如果没有登录则重定向到登录页。

src/router/index.js中添加以下代码:

import { store } from '../store' // 引用一个store用于管理用户状态

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 需要登录才能访问
    if (!store.state.isLoggedIn) {
      next({ name: 'Login', query: { redirect: to.fullPath } })
    } else {
      next()
    }
  } else if (to.matched.some(record => record.meta.requiresAdmin)) {
    // 需要是管理员才能访问
    if (!store.state.isAdmin) {
      next({ name: 'Home' })
    } else {
      next()
    }
  } else {
    next(); // 确保一定要调用 next()
  }
})
  1. router.beforeEach 是一个全局前置守卫,它会在每个路由跳转之前被调用。

  2. 如果目标路由(to)的元数据(meta)包含 requiresAuth 属性,则该路由需要用户登录才能访问。

  3. 如果目标路由的元数据包含 requiresAdmin 属性,则该路由只允许管理员访问。

  4. 如果用户未登录但尝试访问需要登录的路由,将会被重定向到登录页面,并且登录页面会接收到一个查询参数 redirect,用来记录用户原本想要访问的路径。

  5. 如果用户不是管理员但尝试访问需要管理员权限的路由,将会被重定向到首页。

3. 全局解析守卫

我们还可以添加一个全局解析守卫,例如用于预加载数据:

router.beforeResolve((to, from, next) => {
  // 在这里可以预加载数据
  next()
})

三、局部路由守卫

局部路由守卫允许我们在组件内部定义路由守卫。这对于特定组件的数据加载或条件渲染特别有用。

1. 组件内的守卫

在我们的组件内,可以定义beforeRouteEnterbeforeRouteUpdate这样的守卫来控制组件的状态。在Profile.vue中,我们可以添加一个守卫来检查用户是否已经登录。

export default {
  beforeRouteEnter(to, from, next) {
    // 在组件被激活之前调用
    if (!store.state.isLoggedIn) {
      next('/login')
    } else {
      next(vm => {
        vm.fetchProfileData() // 假设这是一个异步加载用户数据的方法
      });
    }
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    if (to.params.userId !== from.params.userId) {
      this.fetchProfileData()
    }
    next()
  },
  methods: {
    fetchProfileData() {
      // 获取用户数据
    }
  }
}
  1. beforeRouteEnter 守卫:

    • 这个守卫在渲染这个组件的路由被激活之前调用。

    • 它检查用户是否已经登录 (store.state.isLoggedIn)。如果没有登录,则重定向用户到登录页面 ('/login')。如果已登录,则等待组件创建完成之后调用 fetchProfileData() 方法来加载用户数据。

  2. beforeRouteUpdate 守卫:

    • 当当前路由改变但该组件实例被复用时会调用这个守卫。

    • 它检查路由参数中的 userId 是否发生了变化 (to.params.userId !== from.params.userId)。如果 userId 发生了变化,则调用 fetchProfileData() 方法来更新用户数据。

  3. fetchProfileData 方法:

    • 这个方法负责获取用户个人资料数据,通常是通过从 API 或者数据存储中获取。
    • 具体实现细节没有给出,但通常它会发起一个异步请求,并将新数据更新到组件的状态中。

需要注意的是,beforeRouteEnter 守卫无法直接访问 this(Vue 实例),因为此时组件还没有被创建。这就是为什么 next 被调用时带有一个回调函数,这个回调函数接收 Vue 实例 (vm) 作为参数。beforeRouteUpdate 守卫可以直接访问 this,因为此时组件已经被创建并处于活动状态。


四、导航守卫

除了全局和局部守卫之外,Vue Router还提供了navigation guards,这是一些更细粒度的守卫,它们允许你在组件实例中拦截导航。

1. 使用 beforeRouteLeave

最后,我们可以在Profile.vue中添加一个beforeRouteLeave守卫,以确保在用户离开页面前执行一些操作,比如保存表单数据。

export default {
  // ...之前的代码
  beforeRouteLeave(to, from, next) {
    if (this.formChanged) {
      // 如果表单有更改,询问用户是否保存
      const answer = window.confirm('确定不保存更改吗?')
      if (answer) {
        next();
      } else {
        next(false)
      }
    } else {
      next()
    }
  },
  data() {
    return {
      formChanged: false,
    }
  },
  methods: {
    // ...之前的代码
    onFormChange() {
      this.formChanged = true
    },
  },
}

首先检查 formChanged 数据属性是否为 true,表示表单数据已发生更改。

如果 formChanged 为 true,则弹出一个确认对话框询问用户是否继续离开而不保存更改。如果用户点击“确定”(answer 为 true),则调用 next() 函数允许导航继续。 如果用户点击“取消”,则调用 next(false) 阻止导航。如果 formChanged 为 false,则直接调用 next() 允许导航继续。

这样我们确保在离开页面前保存了表单数据,可以防止用户在没有保存的情况下丢失数据。


总结

通过今天的介绍,大家应该对Vue 3中的路由守卫有了全面的认识。路由守卫是Vue Router中一项强大的特性,它可以帮助你控制应用的导航流程、数据加载以及用户交互等,我们最后再巩固一下基本认识。

  • 全局守卫:适合全局性的逻辑处理,如权限验证。
  • 局部守卫:用于组件内部,可以用来加载数据或做条件渲染。
  • 导航守卫:可以用来做一些清理工作,如保存表单数据。

希望这篇文章能帮助你更好地理解和运用路由守卫。一起加油!

转载自:https://juejin.cn/post/7395413975976198178
评论
请登录