您的位置:

Vue路由钩子详解

Vue.js 是一个构建数据驱动的 Web 界面的渐进式框架,核心库叫做「Vue」,这个库只关注视图层,采用自底向上增量开发的设计。

Vue.js 官方提供了一个扩展插件 Vue Router,它允许构建 SPA(Single Page Application)单页面应用程序。Vue Router 的作用是将每个路由映射到一个组件,然后将组件渲染到 DOM 中。

Vue Router 允许开发人员在路由改变时添加「钩子函数」,并且支持许多不同类型的钩子。本篇文章将深入解析 Vue 路由的各种钩子函数及其作用。

一、全局前置守卫:beforeEach

使用 Vue Router 时,它提供了多个路由导航钩子,其作用类似于其他框架中路由的生命周期函数。其中,全局前置守卫 `beforeEach` 是最常用的一个。这个钩子函数会在路由即将改变前被调用。我们可以利用这个钩子函数,实现登录、权限验证等场景。

beforeEach 函数接收三个参数:

  1. to: 即将要进入的目标路由对象
  2. from: 当前导航正要离开的路由对象
  3. next: 调用该方法后,才能进入下一个钩子函数,否则路由将会被中断
const router = new Router({
  routes: [
    {
      path: '/admin',
      component: Admin,
      meta: { requiresAuth: true }
    }
  ]
})

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    if (!userLoggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next()
  }
})

上面的代码实现了一个简单的登录权限校验,如果用户未登录,路由将被重定向到登录页;否则,用户将被跳转到下一步操作的路由。

二、全局后置钩子:afterEach

Vue Router 还提供了另外一个路由导航钩子函数 afterEach,这个函数会在路由改变之后被调用。它没有 next 参数,也就不能中断路由,但我们常常可以利用这个钩子函数,实现页面的统计、日志打印等操作。

router.afterEach((to, from) => {
  logPageView(to.path)
})

上面的代码实现了一个简单的页面访问日志打印,每当路由改变后,将会记录下页面的相关信息。

三、组件内守卫

在 Vue Router 中,我们还可以使用组件内路由钩子,这些钩子函数作用于组件级别。常用的组件内路由钩子有这几个:

  • beforeRouteEnter
  • beforeRouteUpdate
  • beforeRouteLeave

这些钩子每次都会在相应的路由导航期间触发。

3.1 beforeRouteEnter

beforeRouteEnter 钩子可以访问组件实例 `this`,但是此时组件实例尚未创建。beforeRouteEnter 钩子中,不能使用 `this.props` 访问组件 props。

如果需要访问组件 props,我们可以通过回调函数,将 props 作为参数来传递。

const Post = {
  props: ['id', 'title'],
  
  beforeRouteEnter (to, from, next) {
    axios.get(`/api/post/${to.params.id}`)
      .then(response => {
        next(vm => {
          vm.post = response.data
        })
      })
  },
  
  data () {
    return {
      post: null
    }
  },
  
  template: `
    
   

{{ post.title }}

` }

上面的代码实现了在路由载入 Post 组件时,请求相应的文章详情。通过 next 回调函数,我们可以将响应结果传递给组件实例的 data 属性。

3.2 beforeRouteUpdate

beforeRouteUpdate 钩子函数,能够监听路由变化,做出相应的更新操作。它也能够访问到组件实例 `this`。

const Post = {
  beforeRouteUpdate (to, from, next) {
    this.loadPost(to.params.id)
    next()
  },
  
  methods: {
    loadPost (id) {
      axios.get(`/api/post/${id}`).then(response => {
        this.post = response.data
      })
    }
  },
  
  data () {
    return {
      post: null
    }
  },
  
  template: `
    
   

{{ post.title }}

` }

上面的代码实现在路由改变时,通过 loadPost 方法更新组件实例的 data 属性,从而使得组件重新渲染。

3.3 beforeRouteLeave

beforeRouteLeave 钩子函数,在离开当前路由时,做出相应的操作。这个钩子函数不能访问组件实例 `this`,但是我们可以通过 `vm` 参数来访问组件实例。

const PostEditor = {
  beforeRouteLeave (to, from, next) {
    if (this.content.length > 0) {
      const doLeave = window.confirm('你还有未提交的内容,确认离开?')
      if (doLeave) {
        next()
      } else {
        next(false)
      }
    } else {
      next()
    }
  },
  
  data () {
    return {
      content: ''
    }
  },
  
  methods: {
    save () {
      axios.post('/api/post', { content: this.content })
    }
  },
  
  template: `
    
   
` }

上面的代码实现了在离开 PostEditor 组件时,检测是否有未提交的内容。如果有,将提示用户确认是否离开,如果用户确认离开,这个钩子函数返回 next();如果用户取消离开,这个钩子函数返回 next(false),路由中断不会离开当前路由。

四、路由独享守卫

路由独享守卫,也就是仅对特定路由进行守卫。我们可以在路由配置对象上,添加 `beforeEnter` 来实现这个特定守卫。

const router = new VueRouter({
  routes: [
    {
      path: '/admin',
      component: Admin,
      beforeEnter: (to, from, next) => {
        if (!userLoggedIn()) {
          next({
            path: '/login',
            query: { redirect: to.fullPath }
          })
        } else {
          next()
        }
      }
    }
  ]
})

上面的代码实现了在用户进入 /admin 这个路由时,会触发独享守卫,检查用户是否已经登录。如果未登录,则会重定向到登录页面;否则,进入目标路由。