问题一
为什么在 created 中使用了 $nextTick 可以通过 Vue 的方式获取 DOM???
首先说一下为什么我会有这个疑问- 在事件循环(Event loop)中,微任务的优先级是要大于 DOM 渲染的
- 而在 Vue 底层对于 $nextTick 的实现,如果浏览器支持 Promise,那么就是用的 Promise
- Promise.then 也是微任务
- 我这里测试是使用的谷歌浏览器,所以 $nextTick === 微任务
<div id="app" ref="app">
<span>{{count}}</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
count: 1
},
created() {
// 这里可以很明显的看到是有的
this.$nextTick(() => {
console.dir(this.$refs.app); // div#app
})
},
})
</script>
- 对于 nextTick,Vue 官网是这样解释的:
- 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。
- 看到这里我恍然大悟,vue 的数据变更是放入异步队列进行的更改,然后使用 nextTick 是最后入队,这样就可以在前面队列更新完成后获取到最新的
<div id="app" ref="app">
<span>{{count}}</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
count: 1
},
created() {
// 这里 DOM 其实早就渲染好了,只是 Vue 在之后的生命周期中又对页面的 DOM 进行了更改
// 也就是经过 Vue 渲染的 DOM 才能使用 this.$refs 的方法去获取
// 因为 Vue 是数据驱动页面,而我们通过原生的方法在 created 中进行获取,
// 很有可能在后面的生命周期又对 DOM 进行了更改,所以最好不要这样去做
console.dir(document.querySelector("#app")); // div#app
console.dir(this.$refs.app); // undefined
},
})
</script>
问题二
- 刚才上面说到了,使用 $nextTick 获取的 DOM,是货真价实经过 Vue 更新 DOM
- 起初我也是这样想的😂,但是前一段时间做项目遇到了一个问题,搞了好久,大概需求就是我有一个 DOM 默认没有高度,它的高度是网络请求的数据来撑开的,然后我在 mounted 生命周期中使用 $nextTick 就是获取不到更新后的 DOM
- 其实这里还是一个时机问题,首先 $nextTick 是微任务,然后网络请求时宏任务
- 网络请求成功之后,才会对数据进行更改,才会再次更新 DOM
mounted() {
fetch("xxx")
.then(res => {
console.log("data");
})
this.$nextTick(() => {
console.log(1111);
})
},
// 1111
// "data"
// 先执行的 111,在执行的网络请求
// 因为数据是在请求之后进行的修改,DOM 也是数据更改之后才会更新的,所以那就拿不到最新的 DOM 了
// 这里想要拿到最新的 DOM,要在 updated 中去获取