Vue的异步加载与生命周期

问题

最近项目上需要通过获取后端传过来的JSON数组,采用的是Vue+axios来异步加载数据,但是在加载的时候遇见了这样的一个问题,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
let defect = new Vue({
el: '#defect',
data: {
defectList: []
},
created() {
axios.get('${ctx}/def/defMessage/listJson').then(response => (this.defectList = response.data))
},
mounted(){
console.log(this.defectList)
}

});

分析

这里先来了解一下Vue实例的生命周期,官方文档是这么说的:链接

vuelife.png

所以这里应该是在Vue实例创建的时候就通过axios异步加载JSON赋值给data中的defectList数组,然而为什么在mouted,也就是Vue实例挂载到DOM上时,输出defectList的时候,输出的是:
image.png
也就是说,当挂载到DOM上的时候,我们的defectList数组还是空值,这又是为什么呢?再回顾一遍异步加载的知识,什么是异步加载?
简要来说就是:

Async is short for “asynchronous”. It’s easier to understand async if you first understand what “synchronous”, the opposite, means. … Asynchronous code takes statements outside of the main program flow, allowing the code after theasynchronous call to be executed immediately without waiting.

即:

异步加载和同步加载相反,异步代码将语句置于主程序流之外,允许在异步调用之后立即执行代码而无需等待

看到这,似乎有点眉目了,那就是代码中的异步加载使得当Vue实例创建时,语句并没有异步执行,而是当实例挂载到DOM之后,才通过更新数据进行渲染,为了验证这一观点,在updated这个钩子内输出defectList数组内容查看是否被更新,updated钩子介绍如下:

  • 类型 function

  • 详细

由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性watcher 取而代之。

注意 updated 不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用 vm.$nextTick 替换掉 updated

1
2
3
4
5
6
updated: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
})
}


将代码加上updated钩子,调用console.log(defectList)查看数组内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let defect = new Vue({
el: '#defect',
data: {
defectList: []
},
created() {
axios.get('${ctx}/def/defMessage/listJson').then(response => (this.defectList = response.data))
},
mounted(){
console.log(this.defectList)
},
updated(){
console.log(this.defectList)
}

});

可以看见这时输出了从后台加载来的JSON数组
image.png

总结

如果在Vue中通过异步加载获取数据时,数据会在实例挂载到DOM之后才加载上,接着对这个节点进行更新,因此在mouted已经之前的钩子中无法获取异步加载的数据