天啦噜,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()
            }
        }
    }
}
相关推荐
cdcdhj1 小时前
vue用通过npm的webpack打包编译,这样更适合灵活配置的项目
vue.js·webpack·npm
运维@小兵4 小时前
vue使用路由技术实现登录成功后跳转到首页
前端·javascript·vue.js
能来帮帮蒟蒻吗5 小时前
VUE3 -综合实践(Mock+Axios+ElementPlus)
前端·javascript·vue.js·笔记·学习·ajax·typescript
Java&Develop6 小时前
Vue ElementUI原生upload修改字体大小和区域宽度
vue.js
郭尘帅6667 小时前
vue3基础学习(上) [简单标签] (vscode)
前端·vue.js·学习
st紫月9 小时前
用vue和go实现登录加密
前端·vue.js·golang
岁岁岁平安9 小时前
Vue3学习(组合式API——计算属性computed详解)
前端·javascript·vue.js·学习·computed·计算属性
工业互联网专业11 小时前
基于springboot+vue的医院门诊管理系统
java·vue.js·spring boot·毕业设计·源码·课程设计·医院门诊管理系统
九月TTS12 小时前
TTS-Web-Vue系列:Vue3实现内嵌iframe文档显示功能
前端·javascript·vue.js
我爱加班、、12 小时前
Chrome安装最新vue-devtool插件
javascript·vue.js·chrome·vue-devtool