天啦噜,Vue生命周期加上async并不会阻塞子组件的加载

问题分析

最近有个需求,需要维护一个状态,这个状态需要在多个地方访问。我一顿操作猛如虎,将这个状态使用vuex管理, 为了让这个状态可以在组件渲染之前加载,我的操作如下:

js 复制代码
// 在APP.vue上调用distpatch去加载用户信息
export default{
     async created(){
        await this.$store.dispatch("user/getUserInfo")
    }
}

在这里,我很细节 的在created前面加了async ,并且await dispatch执行结束。在我印象中,父组件的created会先于子组件的created,那我在APP阻塞住,那就会等待加载好用户信息,然后再开始执行子组件的created。

在我自信满满的提交了代码后,我亲爱的测试给我提了个bug。但是这个bug,我一直都无法复现,在一番友好交流下,发现小可爱居然打开了网络节流。

此后,我发现一个天大的秘密,vue在created上加async和await,并不会阻塞子组件的created,vue调用生命周期hook的方法如下,可以看到是同步的:

js 复制代码
export function callHook( vm: Component, hook: string, args?: any[], setContext = true ) { 
    pushTarget() 
    const prevInst = currentInstance 
    const prevScope = getCurrentScope() 
    setContext && setCurrentInstance(vm) 
    const handlers = vm.$options[hook] 
    const info = `${hook} hook` 
    if (handlers) { 
        for (let i = 0, j = handlers.length; i < j; i++) { 
            invokeWithErrorHandling(handlers[i], vm, args || null, vm, info) 
        } 
     } 
     if (vm._hasHookEvent) { 
         vm.$emit('hook:' + hook) 
     } 
     if (setContext) { 
         setCurrentInstance(prevInst) prevScope && prevScope.on() 
      } 
      popTarget() 
  }

解决方案

  1. 最简单的方法当然是从全局阻塞入手了,app的created入手不行,那我从main.js入手就可以了吧:
js 复制代码
    async function initApp(){
        await this.$store.dispatch("user/getUserInfo")
        return new Vue({
            el: "#app",
            render: h=>h(APP)
        })
    }
  1. 上述方法简单粗暴,但是未登录或token过期时,请求会被拦截,那获取数据将会失败。所以只能麻烦一点,在需要使用的组件里,先调用await this.$store.dispatch("user/getUserInfo"),然后再去执行页面相应逻辑,为此需要优化下getUserInfo方法:
js 复制代码
const EVENT_QUEUE = []
{
    actions:{
        async getUserInfo({commit,state}){
            // 加个字段判断是否加载完数据
            if(state.isLoaded) retrun
            // 当前正在加载,等待加载完成
            else if(state.isLoading){
                retrun new Promise(resolve=>{
                    EVENT_QUEUE.push(resolve)
                })
            }
            
            commit("UPDATE_ISLOADING", true)
            
            const userInfo = await fetchUserInfo()
            commit("UPDATE_ISLOADING", false)
            commit("UPDATE_ISLOADED", true)
            commit("UPDATE_USERINFO", userInfo)
            
            // 清空dispatch列表
            while( EVENT_QUEUE.length > 0 ){
                EVENT_QUEUE.pop().resolve()
            }
        }
    }
}
相关推荐
鸿Hong1 小时前
从事Vue开发之查漏补缺:深度选择器
vue.js
小小前端_我自坚强1 小时前
vue提高技术 高级语法相关
前端·vue.js·前端框架
小小前端_我自坚强1 小时前
Vue 3 使用心得
前端·javascript·vue.js
Keepreal4962 小时前
使用Canvas绘制转盘
javascript·vue.js·canvas
ScriptBIN2 小时前
Javaweb--Vue
前端·vue.js
前端Hardy2 小时前
Vue 高效开发技巧合集:10 个实用技巧让代码简洁 50%+,面试直接加分!
前端·javascript·vue.js
@大迁世界6 小时前
第03章: Vue 3 组合式函数深度指南
前端·javascript·vue.js·前端框架·ecmascript
二十雨辰6 小时前
vite与ts的结合
开发语言·前端·vue.js
我是日安6 小时前
从零到一打造 Vue3 响应式系统 Day 25 - Watch:清理 SideEffect
前端·javascript·vue.js
我的div丢了肿么办6 小时前
vue3使用h函数如何封装组件和$attrs和props的区别
前端·javascript·vue.js