天啦噜,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()
            }
        }
    }
}
相关推荐
加减法原则6 小时前
Vue3 组合式函数:让你的代码复用如丝般顺滑
前端·vue.js
天若有情6736 小时前
React、Vue、Angular的性能优化与源码解析概述
vue.js·react.js·angular.js
草巾冒小子7 小时前
vue3实战:.ts文件中的interface定义与抛出、其他文件的调用方式
前端·javascript·vue.js
eggcode8 小时前
Vue+Openlayers加载OSM、加载天地图
vue.js·openlayers·webgis
武昌库里写JAVA11 小时前
vue3面试题(个人笔记)
java·vue.js·spring boot·学习·课程设计
lalalalalalalala12 小时前
开箱即用的 Vue3 无限平滑滚动组件
前端·vue.js
前端Hardy12 小时前
8个你必须掌握的「Vue」实用技巧
前端·javascript·vue.js
久爱@勿忘12 小时前
第二章:创建登录页面
前端·vue.js·elementplus
Jinxiansen021112 小时前
Vue 3 中父子组件双向绑定的 4 种方式
javascript·vue.js·ecmascript
木依12 小时前
Vue3 Element plus table有fixed列时错行
javascript·vue.js·elementui