beforeRouteEnter 到底是个啥?
咱做Vue2项目时,路由切换的逻辑里经常会碰到beforeRouteEnter这个东西,好多刚学的小伙伴一头雾水:这钩子是干啥的?啥时候用?咋用才对?今天就把beforeRouteEnter的门道掰开揉碎,从基础到实战一次性讲明白~
先搞清楚定位:**beforeRouteEnter是Vue Router给咱提供的「组件内导航守卫」**,导航守卫可以理解成路由切换时的“钩子函数”,能在路由跳转的不同阶段插一脚,做权限判断、数据加载这些事儿。那beforeRouteEnter的触发时机很关键——当你要进入一个组件对应的路由时,这个钩子会在组件实例创建之前就执行,注意哦,是“组件实例创建前”,所以这时候组件的this
是拿不到的(因为实例都没诞生呢)。
举个🌰 你做了个商品详情页组件GoodsDetail
,配置了路由/goods/:id
,用户从商品列表点进详情页时,beforeRouteEnter
就会先触发,这时候GoodsDetail
的实例还没生成,想在钩子里头直接用this.fetchData()
?门儿都没有,this
是undefined
~
咋在项目里用beforeRouteEnter?
用起来不复杂,但得注意细节,先看基本写法:
export default { beforeRouteEnter(to, from, next) { // 第一步:处理逻辑(比如权限、请求数据) // 第二步:必须调用next()决定路由往哪走 next(vm => { // vm是组件实例,这时候能拿到this了! vm.doSomething() }) }, methods: { doSomething() { console.log('组件实例创建后,我被调用啦~') } } }
这里得把to
、from
、next
这仨参数吃透:
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给组件内提供了三个守卫:beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
,得把它们的区别理清楚,不然容易用错场景:
守卫名称 | 触发时机 | 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
在组件实例创建前执行,这时候this
是undefined
,要是写成这样:
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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。