在vue中我们知道在数据更新后,我们想要获取dom进行一些操作的话,是不能直接通过ref获取的,因为这个时候dom并没有更新,所有我们需要等dom更新完了再去获取dom,为什么dom不是数据更新马上同步更新呢?因为如果我们将一个数据修改三次,dom连续更新3次是十分浪费性能的.当数据变化后,把 watcher.update 函数存放进 nextTick 的 回调数组中,并且会做过滤。 通过 watcher.id 来判断 回调数组 中是否已经存在这个 watcher 的更新函数不存在,才 push。 之后 nextTick时 遍历回调数组,便会执行了更新。 所以当三次修改数据的时候,会 push 回调数组 三个 watcher.update,但是只有第一次是 push 成功的,其他的会被过滤掉,因为已经存在了。 所以,不管你修改多少次数据,nextTick 的回调数组中只存在唯一一个 watcher.update,从而页面只会更新一次。
nexttick一定是微任务吗
no,正常情况下都是微任务,但是如果游览器都不支持微任务,那么他肯定不是的. 检测浏览器是否支持Promise,如果支持,则使用Promise来执行回调函数队列,毕竟微任务速度大于宏任务。如果不支持的话,就只能使用宏任务来执行回调函数队列。
// 如果浏览器不支持Promise,使用宏任务来执行nextTick回调函数队列
// 能力检测,测试浏览器是否支持原生的setImmediate(setImmediate只在IE中有效)
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// 如果支持,宏任务( macro task)使用setImmediate
macroTimerFunc = () => {
setImmediate(flushCallbacks)
}
// 同上
} else if (typeof MessageChannel !== 'undefined' && (
isNative(MessageChannel) ||
// PhantomJS
MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
const channel = new MessageChannel()
const port = channel.port2
channel.port1.onmessage = flushCallbacks
macroTimerFunc = () => {
port.postMessage(1)
}
} else {
/* istanbul ignore next */
// 都不支持的情况下,使用setTimeout
macroTimerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
在宏任务的情况下 vue也会根据支持度选择不同的方法执行任务. setImmediate>MessageChannel>setTimeout
// 回调函数队列
const callbacks = []
// 异步锁
let pending = false
// 执行回调函数
function flushCallbacks () {
// 重置异步锁
pending = false
// 防止出现nextTick中包含nextTick时出现问题,在执行回调函数队列前,提前复制备份,清空回调函数队列
const copies = callbacks.slice(0)
callbacks.length = 0
// 执行回调函数队列
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
// 我们调用的nextTick函数
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
// 将回调函数推入回调队列
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
// 如果异步锁未锁上,锁上异步锁,调用异步函数,准备等同步函数执行完后,就开始执行回调函数队列
if (!pending) {
pending = true
if (useMacroTask) {
macroTimerFunc()
} else {
microTimerFunc()
}
}
// $flow-disable-line
// 2.1.0新增,如果没有提供回调,并且支持Promise,返回一个Promise
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
接收回调函数,将回调函数推入回调函数队列中。 判断当前环境是否支持原生Promise,原生MutationObserver,试图把回调放入微任务队列去执行,如果不支持,则检查是否支持setImmediate,不支持就用setTimeout。就是对环境进行一个降级处理,去执行flushCallbacks函数.
我们在实际开发中可以在callback放我们的代码,也可以await nexttick(),因nexttick返回的是一个promise 所以可以等nexttick执行完再执行我们的代码,这样也 可以获取到dom
源码分析参考itwangyang520:blog.csdn.net/itwangyang5… eat老虎滴兔兔:blog.csdn.net/clhdtzhh/ar…