vue.$nextTick 实现原理

402 阅读2分钟

关于事件循环

可以看看这几篇文章

JavaScript中的Event Loop(事件循环)机制

详解JavaScript中的Event Loop(事件循环)机制

宏任务与微任务

image.png

vue.$nextTick 实现

  • 大于2.6 $nextTick的源码>2.6

提交记录:fix: always use microtasks for nextTick #8450

使用了Promise、MutationObserver、setImmediate、setTimeout

  • 小于2.6大于等于2.5 $nextTick的源码>2.5

使用了setImmediate、MessageChannel、setTimeout、promise

提交记录:fix: use MessageChannel for nextTick

  • 小于2.5

$nextTick的源码<2.5

使用了Promise、MutationObserver、setTimeout

2.6.x的版本实现

这里有这么一大堆注释,我翻译下吧。

image.png

这里我们有使用微任务的异步延迟包装器。在2.5的版本中,我们使用了(宏观)任务(与微观任务相结合)(注:2.5x,依次使用setImmediate(宏)、MessageChannel(宏)、setTimeout(宏)、promise(微)),但是在重绘前改变状态时,会出现一些微妙的问题。

比如(e.g. #6813, out-in transitions,注:这个问题我实在没有看出来,v-show是重绘(display:none),v-if是重排)。 在事件处理程序中使用(宏)任务会导致一些奇怪的行为。那是我们绕不过去的(e.g. #7109, #7153, #7546, #7834, #8109,注:以上问题我只看了#7546,在2.5的版本确实ipad,iphone确实要点击两次才能弹出键盘,在2.4的版本只需要点一次),所以我们现在处处使用微任务。这种取舍的一个主要缺点是,有些场景下,微任务的优先级太高,在错综复杂的事件之间会出错(e.g. #4521, #6690, which have workarounds),即使在同一事件的两次冒泡间(#6566)。

fire in between supposedly sequential events or even between bubbling of the same event 这句英文翻译不过来,啊啊啊

image.png nextTick行为利用微任务队列,可以通过原生Promise.then或MutationObserver来访问。MutationObserver有更广泛的支持,但是在iOS >= 9.3.3的UIWebView中,当在触发触摸事件,它有严重的bug。触发几次后就完全停止工作了......所以,如果有支持原生的Promise,我们就用Promise。

image.png 在有问题的UIWebViews中,Promise.then并没有完全崩溃,但它可能会卡在一个奇怪的状态,即回调被推送到微任务队列中,但队列并没有被刷新,直到浏览器需要做一些其他工作,例如处理一个计时器。因此我们可以通过添加一个空的时间来 "强制 "微任务队列被刷新。

小惊喜

我发现 vue判断是否支持原生

function isNative (Ctor) {
  return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}
isNative(Promise)
isNative(MutationObserver)
isNative(setImmediate)

反正我是想不到这么写,哭唧唧~

留给自己的思考

#7546。对于这个问题,暂时还没从源码上想明白,为啥在ipad上2.4版本只要点击一次,2.5版本需要点击两次,呜呜。 而两个版本的区别在于

  • 2.4版本依次使用setImmediate(宏)、MessageChannel(宏)、setTimeout(宏)、promise(微)
  • 2.5版本依次使用Promise、MutationObserver、setImmediate、setTimeout

其他链接

MutationObserver

MessageChannel

Vue.js 升级踩坑小记

简单理解Vue中的nextTick

事件循环模拟网站

冴羽的博客

以上有错误,请指出,欢迎补充