vue的nextTick,为什么微任务处理的时候能获取到更新后的DOM

我一直想不通一个问题:

UI渲染是宏任务,nextTick如果是微任务处理(Promise.then)的时候,是在UI渲染之前执行的,那时候浏览器都还没有把新的DOM和CSSOM组合,那它是怎么拿到更新后的 Dom 内容的?

阅读 5.1k
2 个回答

ui渲染的确是在微任务后面,但是dom操作都是同步的啊

document.body.style.background = 'red'
console.log(document.body.style.background)

就像上面这个代码打印的时候的确是还没把body渲染成红色,但是此时dom已经改变了,至于只有真正渲染后才能得到的值,比如元素的宽高度,你再调用getBoundingClientRect时会导致浏览器实时去重绘,所以你也可以获得更新后的值,前提时你已经对dom进行了操作

而在vue中,比如如下的代码,dom操作也是在微任务里执行的,在你用nextTick入队的方法执行的时候对dom的操作已经完成了(看下面的调用栈),因为vue里dom的操作也是放在微任务队列里的所以你要用nextTick入队的代码只要写在在对data赋值后面就能在dom操作后面后执行,那么你就能获得更新后的dom

<template>
  <div id="app">
    <button @click="setListAndComputeNewHeight">
      setListAndComputeNewHeight
    </button>
    <ul ref="container">
      <li v-for="item in list" :key="item">{item}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      list: [],
    };
  },
  methods: {
    setListAndComputeNewHeight() {
      const container = this.$refs.container;
      this.list = Array.from({ length: 10 }, (_, i) => i);

      this.$nextTick(() => {
        console.log("after", container.childNodes.length);
      });
    },
  },
};
</script>

<style>
li {
  height: 20px;
  margin-bottom: 5px;
  background: indigo;
}
</style>

对data赋值后的调用栈,其中在run中执行dom操作
image.png

我的理解是:“更新后的DOM” 其实是指vue 批量异步更新后的DOM数据,虽然UI渲染此时还没更新到视图上,但是DOM树的数据结构是可以通过this访问到的,vue的批量异步更新也是一个微任务,官方文档是这样说的:Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变,同步代码执行完后,会先批量异步更新DOM数据,再执行nextTick。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏