Vue Router 中全局守卫和组件守卫的使用方式及示例

Vue Router 是 Vue.js 官方提供的路由管理器,它可以用来实现单页面应用(SPA)中的前端路由。在 Vue Router 中可以定义多个路由,每个路由都映射到一个组件,当用户访问对应的路由时,会加载对应的组件并显示在页面上。

在 Vue Router 中,路由配置是一个数组,里面包含了多个路由对象。每个路由对象包含了以下属性:

  • path:表示当前路由匹配的 URL 路径。可以是一个字符串,例如 '/home',也可以是一个包含动态参数的路径,例如 '/user/:id',动态参数可以通过 $route.params 访问;
  • name:路由的名称,用来在程序中标识路由。名称必须是唯一的,不能与其他路由重名;
  • component:路由对应的组件。可以是一个引入的组件对象,例如 import Home from './views/Home.vue',也可以是一个函数,例如 () => import('./views/Home.vue'),用于按需加载组件;
  • meta:路由的元数据,用来存储一些额外信息,例如页面标题、页面描述等。

在 Vue Router 中,路由配置通常是在一个单独的文件中定义,例如 router.js。在这个文件中,我们需要先引入 Vue 和 Vue Router,并创建一个新的 Router 实例,然后将路由配置传递给 Router 实例的 routes 属性。最后,我们需要将 Router 实例导出,以便在应用中使用。

下面是一个简单的路由配置示例:

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
import User from './views/User.vue'

Vue.use(Router)

export default new Router({
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', name: 'home', component: Home },
    { path: '/about', name: 'about', component: About },
    { path: '/user/:id', name: 'user', component: User },
    { path: '*', redirect: '/home' }
  ]
})

在上面的示例中,我们创建了一个新的 Router 实例,并将路由配置传递给了 routes 属性。这个路由配置包含了四个路由对象,分别对应了 /home/about/user/:id 三个路径,以及一个通配符路径 '*',用于匹配所有未定义的路径。其中,redirect 属性表示重定向到其他路由。

在 Vue 应用中,我们可以通过 router-link 组件来实现路由的跳转。这个组件可以接受一个 to 属性,用来指定要跳转的路由路径。例如:

Home
About
User

除了使用 router-link 组件来实现路由跳转外,我们还可以在组件中使用 this.$routerthis.$route 来访问 Router 实例和当前路由信息。

例如,在一个组件的方法中,我们可以通过以下方式来实现路由跳转:

this.$router.push('/home')
this.$router.push({ name: 'about' })
this.$router.push({ path: '/user/123' })

在上面的代码中,我们使用了 $router.push 方法来实现路由的跳转。这个方法接受一个路由描述符作为参数,可以是一个字符串路径,也可以是一个对象,包含了路径、名称、参数等信息。通过这种方式,我们可以动态地修改路由,并实现页面跳转。

除了 $router$route,我们还可以使用路由守卫来实现更复杂的路由控制。在 Vue Router 中,路由守卫分为全局守卫和组件守卫两种类型。全局守卫可以监听整个路由的生命周期,包括路由的导航、组件的激活和销毁等;而组件守卫则只监听当前组件的生命周期。

组件守卫

在 Vue Router 中,组件守卫是一种用来监听组件生命周期的钩子函数,可以在路由切换的过程中进行一些处理。组件守卫包括 beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave 三个钩子函数,分别对应组件的进入、更新和离开时调用。

具体来说,这三个组件守卫的作用分别如下:

  • beforeRouteEnter(to, from, next):在组件被激活之前调用。这个钩子函数无法访问组件实例,因为在此时组件实例尚未被创建。但是我们可以通过传递一个回调函数 next,在组件实例创建之后再进行一些操作。这个回调函数可以接受一个组件实例作为参数,可以用来进行一些异步操作,例如加载数据。

    beforeRouteEnter(to, from, next) {
      // 无法访问组件实例
      next(vm => {
        // 在组件实例创建之后调用
        // 可以访问组件实例
        vm.loadData()
      })
    }
  • beforeRouteUpdate(to, from, next):在组件复用时调用。这个钩子函数可以访问组件实例,可以根据路由参数的变化,更新组件内部的状态。注意,如果组件在路由切换时不需要重新渲染,就不会触发这个钩子函数。

    beforeRouteUpdate(to, from, next) {
      if (to.params.id !== from.params.id) {
        // 更新组件内部的状态
        this.loadData()
      }
      next()
    }
  • beforeRouteLeave(to, from, next):在组件被离开之前调用。这个钩子函数可以用来进行一些确认操作,例如弹出对话框。注意,如果我们要在这个钩子函数中进行异步操作,必须在 next 回调函数中进行,否则路由切换会被阻塞。

    beforeRouteLeave(to, from, next) {
      if (this.isDirty) {
        if (confirm('您还有未保存的内容,确定要离开吗?')) {
          next()
        } else {
          next(false)
        }
      } else {
        next()
      }
    }

需要注意的是,组件守卫只对当前组件有效,不会影响其他组件。如果需要在整个应用程序中进行一些操作,可以使用全局守卫。另外,在使用组件守卫时,我们还可以通过在路由配置中添加 meta 字段,来传递一些额外的信息。例如:

const routes = [
  {
    path: '/user/:id',
    component: User,
    meta: { requiresAuth: true }
  }
]

在这个例子中,我们在路由配置中添加了一个 meta 字段,表示这个路由需要进行身份验证。接下来,在组件守卫中就可以通过访问 to.meta.requiresAuth 来获取这个信息了:

beforeRouteEnter(to, from, next) {
  if (to.meta.requiresAuth && !auth.loggedIn()) {
    next('/login')
  } else {
    next()
  }
}

在这个例子中,如果当前路由需要进行身份验证,但是用户没有登录,就会被重定向到登录页面。

最后,需要注意的是,组件守卫和全局守卫一样,也可以通过返回一个 Promise 对象来进行异步操作。如果在守卫中返回了一个 Promise,Vue Router 就会等待这个 Promise 对象解决之后再进行路由切换。例如:

beforeRouteEnter(to, from, next) {
  return axios.get('/user/' + to.params.id).then(response => {
    next(vm => {
      vm.user = response.data
    })
  })
}

在这个例子中,我们通过 axios 发送了一个 GET 请求,来加载用户的数据。由于这个请求是异步的,因此我们在组件守卫中返回了一个 Promise 对象,来告诉 Vue Router 等待请求完成之后再进行路由切换。

全局守卫

Vue Router 的全局守卫是指在整个路由系统中都起作用的路由守卫。全局守卫包括了三个函数:

  • beforeEach
  • beforeResolve
  • afterEach

beforeEach

beforeEach 是最常用的全局守卫,它会在每次路由跳转之前调用。beforeEach 接收三个参数:

  • to:即将跳转的路由信息对象
  • from:当前的路由信息对象
  • next:一个函数,用于控制路由跳转行为

beforeEach 中,我们可以根据路由信息对象来判断用户是否有权限访问某个页面,或者是否需要进行登录等操作。如果需要进行操作,可以调用 next 函数来进行路由跳转。

下面是一个例子,当用户访问 /dashboard 页面时,需要进行身份验证:

router.beforeEach((to, from, next) => {
  if (to.path === '/dashboard' && !auth.loggedIn()) {
    next('/login')
  } else {
    next()
  }
})

在这个例子中,如果用户访问的是 /dashboard 页面,但是用户没有登录,则会被重定向到登录页面。

beforeResolve

beforeResolvebeforeEach 类似,它也是在路由跳转之前调用,但是它会在 beforeEach 之后调用。在 beforeResolve 中,我们可以进行一些异步操作,例如加载数据等。

afterEach

afterEach 是在路由跳转之后调用的函数,它没有接收 next 函数。afterEach 主要用于处理一些全局的 UI 交互,例如设置页面标题等。

下面是一个例子,当用户访问成功时,设置页面标题:

router.afterEach((to, from) => {
  document.title = to.meta.title || 'My App'
})

在这个例子中,如果在路由配置中设置了 meta.title,则会设置页面标题为该值,否则设置为默认值 My App

需要注意的是,全局守卫是在整个路由系统中都起作用的,因此需要谨慎使用。如果在全局守卫中进行了复杂的逻辑操作,可能会影响整个应用的性能。建议在使用全局守卫时,根据具体场景进行判断,避免不必要的性能损失。

最后修改:2023 年 08 月 27 日
如果觉得我的文章对你有用,请随意赞赏