Vue.nextTick 原理简述

540 阅读1分钟

nextTick 在项目中的使用?

将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它

...
'<div>{{ message }}</div>'
...
import { createApp, nextTick } from "vue";
const app = createApp({
  setup() {
    const message = ref("Hello!");
    const changeMessage = async (newMessage) => {
      message.value![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d736591f5f949f9a6d74c027743e0f1~tplv-k3u1fbpfcp-watermark.image) = newMessage;
      await nextTick();
      console.log("Now DOM is updated");
    };
    return {
      message,
    };
  },
});

nextTick 的原理

浏览器只会在一个 task 结束的最后进行一次渲染, 浏览器的 eventloop 如下图所示:

图中可知,执行顺序为:microTask > UI render > macroTask

nextTick 获取到的是 更新后的 DOM, 使用 setTimeout(function) 能实现 nextTick 的效果。但是 setTimeout 实现 nextTick 有个问题,两个 setTimeout 的最小间隔约为 4ms,这就强行限制了两次渲染之间的最小时间差。

Vue 的 nextTick 通过 Promise(microTask)实现: 因为有虚拟 DOM,所以即使在 UI 渲染前,也能够实现同样的效果,且不会限制两次渲染时间

在 Vue2.5 以后的源码中, Vue nextTick 的降级策略为: microTask(Promise) > macroTask(setImmediate) > macroTask(MessageChannel) > macroTask(setTimeout)

模拟实现 nextTick

setTimeout(f)

setTimeout(f) 还有个更加广泛的用途:切分 cpu 密集型任务.

假设有个任务,从 1 数到 100000000,JS 如果直接执行一个 for loop 或者 while loop 那么很大可能会直接显示:当前页面未响应。

let i = 0;

let start = Date.now();

function count() {
  // do a piece of the heavy job (*)
  do {
    i++;
  } while (i % 1e6 != 0);

  if (i == 1e9) {
    alert("Done in " + (Date.now() - start) + "ms");
  } else {
    setTimeout(count); // schedule the new call (**)
  }
}

count();

参考: