天啦噜,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()
            }
        }
    }
}
相关推荐
正一品程序员4 分钟前
vue项目引入GoogleMap API进行网格区域圈选
前端·javascript·vue.js
star_111215 分钟前
Jenkins+nginx部署前端vue项目
前端·vue.js·jenkins
JIngJaneIL24 分钟前
农产品电商|基于SprinBoot+vue的农产品电商系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·毕设·农产品电商系统
c***727429 分钟前
SpringBoot + vue 管理系统
vue.js·spring boot·后端
JIngJaneIL34 分钟前
书店销售|书屋|基于SprinBoot+vue书店销售管理设计与实现(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·毕设·书店销售管理设计与实现
一 乐1 小时前
农产品销售|农产品供销|基于SprinBoot+vue的农产品供销系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·spring boot
一 乐1 小时前
助农服务系统|基于SprinBoot+vue的助农服务系统(源码+数据库+文档)
前端·数据库·vue.js
by__csdn1 小时前
Vue 2 与 Vue 3:深度解析与对比
前端·javascript·vue.js·typescript·vue·css3·html5
小二·1 小时前
DevUI 和 MateChat:2025 年,我们是怎么把前端开发变轻松的
开发语言·javascript·vue.js
一 乐2 小时前
游戏账号交易|基于Springboot+vue的游戏账号交易系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端·游戏