Code前端首页关于Code前端联系我们

beforeRouteEnter 到底是个啥?

terry 6小时前 阅读数 10 #Vue

咱做Vue2项目时,路由切换的逻辑里经常会碰到beforeRouteEnter这个东西,好多刚学的小伙伴一头雾水:这钩子是干啥的?啥时候用?咋用才对?今天就把beforeRouteEnter的门道掰开揉碎,从基础到实战一次性讲明白~

先搞清楚定位:**beforeRouteEnter是Vue Router给咱提供的「组件内导航守卫」**,导航守卫可以理解成路由切换时的“钩子函数”,能在路由跳转的不同阶段插一脚,做权限判断、数据加载这些事儿。

那beforeRouteEnter的触发时机很关键——当你要进入一个组件对应的路由时,这个钩子会在组件实例创建之前就执行,注意哦,是“组件实例创建前”,所以这时候组件的this是拿不到的(因为实例都没诞生呢)。

举个🌰 你做了个商品详情页组件GoodsDetail,配置了路由/goods/:id,用户从商品列表点进详情页时,beforeRouteEnter就会先触发,这时候GoodsDetail的实例还没生成,想在钩子里头直接用this.fetchData()?门儿都没有,thisundefined

咋在项目里用beforeRouteEnter?

用起来不复杂,但得注意细节,先看基本写法:

export default {
  beforeRouteEnter(to, from, next) {
    // 第一步:处理逻辑(比如权限、请求数据)
    // 第二步:必须调用next()决定路由往哪走
    next(vm => {
      // vm是组件实例,这时候能拿到this了!
      vm.doSomething()
    })
  },
  methods: {
    doSomething() {
      console.log('组件实例创建后,我被调用啦~')
    }
  }
}

这里得把tofromnext这仨参数吃透:

  • to:要进入的目标路由对象(包含路径、参数、元信息这些);
  • from:当前要离开的路由对象;
  • next必须调用的函数,决定路由咋跳转,它有几种玩法:
    • next():正常进入目标路由;
    • next(false):取消跳转,留在当前页面;
    • next('/path')next({ name: 'RouteName' }):跳转到指定路由;
    • next(vm => { ... }):等组件实例创建后,执行回调(这时候vm就是组件的this,能调组件方法、改数据)。

举个实际开发的场景:用户进个人中心页前,得先判断有没有登录,代码可以这么写:

beforeRouteEnter(to, from, next) {
  const token = localStorage.getItem('token')
  if (token) {
    next() // 有token,正常进页面
  } else {
    next({ name: 'Login' }) // 没token,跳登录页
  }
}

和其他组件内守卫有啥不一样?

Vue Router给组件内提供了三个守卫:beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave,得把它们的区别理清楚,不然容易用错场景:

守卫名称 触发时机 this是否可用 典型场景
beforeRouteEnter 进入新组件路由前(实例未创建) 不可用 进入页面前权限验证、预加载数据
beforeRouteUpdate 路由参数变化(组件复用) 可用 同组件内参数变化后刷新数据
beforeRouteLeave 离开当前组件路由前(实例存在) 可用 离开页面前弹窗确认、保存表单

举个beforeRouteUpdate的例子:用户从/user/1跳到/user/2(同一个User组件,只是参数变了),这时候beforeRouteUpdate会触发,而且this能直接用,方便更新用户信息。

beforeRouteLeave更简单,比如用户填了表单要离开,用它来判断是否弹窗:

beforeRouteLeave(to, from, next) {
  if (this.form.isDirty) {
    if (window.confirm('表单没保存,确定离开?')) {
      next()
    } else {
      next(false)
    }
  } else {
    next()
  }
}

实际开发哪些场景适合用beforeRouteEnter?

这钩子在项目里能解决不少实际问题,说几个高频场景:

权限验证(最常见)

后台管理系统里,很多页面只有登录用户能进,用beforeRouteEnter在路由进入前拦一下,检查token或权限:

beforeRouteEnter(to, from, next) {
  const userRole = localStorage.getItem('role')
  // 假设只有admin能进后台
  if (userRole === 'admin') {
    next()
  } else {
    next({ name: 'Home' }) // 没权限跳首页
  }
}

数据预加载(提升体验)

比如商品详情页,用户点进来前,先把数据请求好,避免页面加载后再请求导致的“闪烁”,这时候结合next的回调:

beforeRouteEnter(to, from, next) {
  // 从路由参数里拿商品id
  const goodsId = to.params.id
  axios.get(`/api/goods/${goodsId}`)
    .then(res => {
      // 请求成功后,把数据给组件实例
      next(vm => {
        vm.goodsData = res.data
      })
    })
    .catch(err => {
      console.log('请求失败', err)
      next(false) // 请求失败,不进入详情页
    })
}

埋点统计(隐性需求)

产品经理要统计用户进入页面的来源、时间?用beforeRouteEnter在用户进入时记日志:

beforeRouteEnter(to, from, next) {
  // 记录进入时间、来源路由
  const enterTime = new Date().getTime()
  const fromPath = from.path
  // 调用埋点接口(假设叫trackEvent)
  trackEvent('page_enter', { enterTime, fromPath, page: to.path })
  next() // 正常进入页面
}

用beforeRouteEnter容易踩哪些坑?

新手刚用的时候,很容易栽在这些细节上,提前避坑:

想直接用this?没门儿!

因为beforeRouteEnter在组件实例创建前执行,这时候thisundefined,要是写成这样:

beforeRouteEnter(to, from, next) {
  this.fetchData() // 大错特错!this是undefined
  next()
}

页面直接报错。正确姿势是通过next(vm => vm.fetchData()),等组件实例创建后再调方法。

忘了调next(),路由直接“卡死”

导航守卫里必须调用next(),不然路由跳转流程就断了,页面永远停在那不动,比如你写了权限判断,但忘记处理else分支的next

beforeRouteEnter(to, from, next) {
  const token = localStorage.getItem('token')
  if (token) {
    next()
  }
  // 这里没token的时候,没调next,路由就卡这儿了!
}

得保证所有分支都有next调用,比如加个else next({ name: 'Login' })

异步操作没等完,数据没加载就进页面

如果在beforeRouteEnter里做异步请求(比如调接口),得等请求完再调next,不然可能出现“页面先渲染,数据后加载”的闪烁问题,看错误示例:

beforeRouteEnter(to, from, next) {
  axios.get('/api/data').then(res => {
    // 这里处理数据,但没把next放里面!
  })
  next() // 不管请求完没,直接进页面,数据可能还没到
}

正确写法是把next包在异步回调里:

beforeRouteEnter(to, from, next) {
  axios.get('/api/data')
    .then(res => {
      // 处理数据后再调next
      next(vm => {
        vm.data = res.data
      })
    })
    .catch(err => {
      next(false) // 请求失败,不进入
    })
}

结合Vuex玩更复杂的逻辑咋整?

项目大了,数据管理靠Vuex。beforeRouteEnter里也能结合Vuex做权限、数据加载,比如用户信息存在Vuex里,进入页面前检查是否有用户信息,没有就去请求:

import { mapState } from 'vuex'
// 假设已引入store实例
import store from '@/store' 
export default {
  computed: {
    ...mapState(['userInfo'])
  },
  beforeRouteEnter(to, from, next) {
    // 组件实例没创建,不能用this.$store,直接调引入的store
    store.dispatch('fetchUser')
      .then(() => {
        // 请求成功,进入页面
        next()
      })
      .catch(() => {
        // 请求失败,跳登录
        next({ name: 'Login' })
      })
  }
}

这里得注意:组件实例没创建时,不能通过this.$store访问Vuex,得直接引入store文件,再调action,等action执行完(用户信息拿到了),再调next()放行。

看完这些,再碰到beforeRouteEnter应该不慌了吧?总结下核心点:它是路由进入前的钩子,组件实例没创建所以拿不到this,得靠next的回调;主要用来做权限、预加载、埋点这些事儿;和其他守卫的区别得记准触发时机和this是否可用;用的时候别忘调next,别在异步里提前放行… 现在可以去项目里试试,比如给详情页加个进入前的权限判断,或者预加载数据,实践一遍就更熟啦~要是还有疑问,评论区喊我,咱再唠~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门