1 事件循环 每次event loop的最后,会有一个UI render步骤,也就是更新DOM。标准为什么这样设计呢?考虑下面的代码:
for(let i=0; i<100; i++)
{
dom.style.left = i + 'px';
}
浏览器会进行100次DOM更新吗?显然不是的,这样太耗性能了。事实上,这100次for循环同属一个task,浏览器只在该task执行完后进行一次DOM更新
那我们的思路就来了:只要让nextTick里的代码放在UI render步骤后面执行,岂不就能访问到更新后的DOM了?
vue的数据响应过程包含:数据更改->通知Watcher->更新DOM。而数据的更改不由我们控制,可能在任何时候发生。如果恰巧发生在repaint之前,就会发生多次渲染。这意味着性能浪费,是vue不愿意看到的。
vue进行DOM更新内部也是调用nextTick来做异步队列控制。而当我们自己调用nextTick的时候,它就在更新DOM的那个microtask后追加了我们自己的回调函数,从而确保我们的代码在DOM更新后执行.下面是VUE源码watcher对象更新操作源码,调用了nextTick,所以我们调用nextTick之前给data赋值的对应DOM操作会创建对应的microTask,
Vue 数据拦截的Watcher对象,update方法
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
**queueWatcher**(this)
}
}
而该方法也是使用nextTick来执行
const id = watcher.id
if (has[id] == null) {
has[id] = true
if (!flushing) {
queue.push(watcher)
} else {
// if already flushing, splice the watcher based on its id
// if already past its id, it will be run next immediately.
let i = queue.length - 1
while (i > index && queue[i].id > watcher.id) {
i--
}
queue.splice(i + 1, 0, watcher)
}
// queue the flush
if (!waiting) {
waiting = true
if (process.env.NODE_ENV !== 'production' && !config.async) {
flushSchedulerQueue()
return
}
**nextTick**(flushSchedulerQueue)
}
}
}