你真的了解 nextTick 吗

73 阅读2分钟

关于 Vue2 中 nextTick 的主要原理,具体细节就不在这里展开说了,这里只是想和大家分享一些概念

先提出几个小问题

  1. dom 更新和 dom 渲染是一回事吗?
  2. 众所周知 dom 更新是异步的,这里提到的异步到底是不是 JS 真正的异步事务?

下面解答一下

// 只是写一些伪代码来证明一下
const arr = [1, 2, 3]
// 点击事件
async handleClick() {
      this.arr.push(4)
      this.arr.push(5)
      console.log(this.arr.length) // 3 数据更新是同步的
      const oBox = document.getElementById("oBox")
      console.log(oBox.children.length) //5 dom还没更新

      // dom 更新和 dom 渲染不是一个意思
      // 内存中的dom更新是异步的,但是这个异步并不是真正js的异步事务,而是vue内部维护的,放在某个队列中等待执行的(性能问题,所有的dom放在一起更新)
      Promise.resolve().then(() => {
        // 开启一个微任务
        // 在一个loop里面。到这里所有的同步代码都走完了,dom也更新了(在所有同步代码之后执行,并不是真正的异步事务)
        console.log(oBox.children.length) // 5 这里是从内存中取的值

        alert("1") // 这里中断了但是页面还没有渲染 仍然是3

        // 执行顺序:同步代码 => dom 更新 => 微任务 => dom 渲染 => 宏任务
      })
      // nextTick内部封装了Promise.then(微任务) => MutationObserver(微任务) => setImmediate(宏任务) => setTimeout(宏任务)
      // 根据执行环境一步步判断,前面的没有就执行后面的setImmediate等
      this.$nextTick().then(() => {
        console.log(oBox.children.length) // 5 
        // 如果有 Promise.then(微任务)环境,开启微任务,dom 仍然没有渲染 页面显示 3,获取是内存中的 dom
        alert('xx')// 中断一下,这里页面渲染的仍然是 3
      })
    },

注意这里的dom更新还是内存中的真实dom,不然怎么获取字段,虚拟 dom 的 dom diff 在这个内存中的dom 更新之前