天啦噜,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()
            }
        }
    }
}
相关推荐
人间观察员2 小时前
如何在 Vue 项目的 template 中使用 JSX
前端·javascript·vue.js
雪中何以赠君别2 小时前
Vue 2 与 Vue 3 双向绑定 (v-model) 区别详解
前端·javascript·vue.js
林太白2 小时前
Vue3-ElementPlus使用
前端·javascript·vue.js
不一样的少年_2 小时前
这才是 Vue 驱动的 Chrome 插件工程化正确打开方式
vue.js·chrome·typescript
胡八一5 小时前
使用qianjkun uniapp 主应用 集成 vue微应用
前端·vue.js·uni-app
blueblood6 小时前
在 Ant Design Vue 2 中隐藏 a-modal 右下角自带的确定按钮
前端·vue.js
那你能帮帮我吗6 小时前
el-tree过滤后的数据,选择父节点,仅选中过滤后的子节点
vue.js·element
玖伍贰零壹肆6 小时前
vue——解决跨层级组件通信难题
前端·javascript·vue.js
BillKu8 小时前
Vue3组件加载顺序
前端·javascript·vue.js
叫我阿柒啊8 小时前
Java全栈工程师的实战面试:从基础到微服务的全面解析
java·数据库·vue.js·spring boot·微服务·前端开发·全栈开发