浅谈 vue 在 created 和 mounted 请求异步数据的区别

前言

今天下班,日常在公交车上用手机浏览技术社区,刷文章。偶然看到一篇文章,其中一部分内容如下图:
浅谈 vue 在 created 和 mounted 请求异步数据的区别
其大意是在 created 这个生命周期请求异步数据的话,请求过多,页面会长时间处于白屏。看到这我就不淡定了,请求不是异步的?怎么会影响到渲染呢?EventLoop 白学了?之后我将这张截图发给朋友讨论了一波,发现还是有挺多人搞不清楚其中的关键,都说在 mounted 阶段请求数据会比较好,在 created 请求可能会找不到需要渲染的元素之类的。于是就有了这篇文章的诞生。


其他 “谣言”

对于这个问题,我去搜索了一波,发现网上对此还有颇多的“谣传”。很多都说在 mounted 发送异步数据请求比较好,具体的原因大致分为以下几类。

谣言1: 官方建议数据异步请求放在 mounted 钩子函数中进行

浅谈 vue 在 created 和 mounted 请求异步数据的区别

官方有说过这句话?我翻遍了整个官方文档,也没发现官方在哪里建议在 mounted 钩子函数中进行异步数据请求。

谣言2: created 阶段的异步数据请求与 mounted 请求的区别:前者页面视图未出现,如果请求数据过多,页面会长时间处于白屏状态

浅谈 vue 在 created 和 mounted 请求异步数据的区别

首先,要明白请求是异步的,不会阻塞到页面的渲染。vue 生命周期钩子函数中的异步请求,会放在事件队列里面,不影响静态页面的加载,白屏只是因为组件加载得慢。

谣言3: 在 created 中请求数据,可能 Vue 的虚拟 DOM 已经加载完成,但请求尚未完成,等请求响应完成,又得重新更新一遍虚拟 DOM,白屏时间长。而在 mounted 中发异步数据请求则只需更新部分 DOM 即可。

浅谈 vue 在 created 和 mounted 请求异步数据的区别

这种说法,和上面一种类型,典型的只考虑了 vue 的生命周期,没有考虑到 JavaScript 的 EventLoop 。vue 生命周期钩子函数中的异步操作会放在事件队列中,也就是说不会在这个钩子函数中执行。所以在 created 和 mounted 中请求异步数据是一样的,两者都不会立即进行数据更新,所以不会导致虚拟 DOM 的重载,更不会影响到真实视图。

谣言4:异步数据请求有快有慢,在 created 中请求的异步数据万一响应很快,此时 DOM 未完成渲染,找不到它需要渲染的 DOM 元素,会出现一些 bug。而在 mounted 中发送请求则不会。

浅谈 vue 在 created 和 mounted 请求异步数据的区别

这种说法,纯属扯蛋。vue 生命周期钩子函数中异步数据的赋值,是不会在该钩子函数中执行的,而是在一遍流程结束后,才会处理其中的异步操作。所以本阶段不会触发数据更新,也不会触发虚拟 DOM 重新载入,更不存在找不到 DOM 元素渲染。


实践检验真理

虽然前面已经说的很清楚了,但实践是检验真理的唯一标准,还是直接上代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const app = new Vue({
el: '#app',

data: {
num: 0
},

async created() {
console.log('created')
this.num = await this.getData('created')
console.log('获取异步数据结束')
console.timeEnd('created获取异步数据完成时长')
},

beforeMount() {
console.log('beforeMounted')
},

async mounted() {
console.log('mounted')
this.num = await this.getData('mounted')
console.log('获取异步数据结束')
console.timeEnd('mounted获取异步数据完成时长')
},

updated() {
console.log('updated')
},

methods: {
// 模拟异步请求
getData(lifecycle) {
console.log(`${lifecycle}开始获取异步数据`)
if (lifecycle === 'created') {
console.time('created获取异步数据完成时长')
} else {
console.time('mounted获取异步数据完成时长')
}
const genRandomNum = (min, max) => (Math.random() * (max - min + 1) | 0) + min
return new Promise(resolve => {
setTimeout(() => {
resolve(genRandomNum(100, 3000))
}, 2000)
})
}
},
})

以上代码运行结果如下图所示,可以看到,在 created 和 mounted 中请求异步数据,都不会立即触发数据的更新,并且在这两个生命周期中获取异步数据更新的时长可以说是几乎一致的。vue 生命周期的钩子函数中的异步操作,都会在一遍流程走完之后,才会进行数据赋值操作,触发 update ,此外 DOM 的更新也是异步的,监听数据变化时,启动一个队列,进行视图更新。

vue生命周期异步.png


结论

  1. 在 created 或 mounted 钩子函数中请求数据都是可以的。
  2. 在 vue ssr 中,vue 的生命周期中没有 mounted 这个钩子函数,所以 ssr 中无法在 mounted 获取异步数据。
  3. 宏任务之后是微任务,微任务过后是布局渲染,此时再执行请求后的回调并完成赋值。created 和 mounted 的区别就出现了,这样相比,created 中请求比 mounted 更快一点。
  4. 也要考虑业务场景,需要 DOM 触发的请求,放在 created 中去,怎么放都请求不到。
  5. 做学问要严谨,如有不对,望指出,感激不尽。
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×