新闻资讯
Vue版本升级nextTick的变化
独立文件
首先是从Vue 2.5+开始,抽出来了一个单独的文件next-tick.js来执行它。
microTask与macroTask
在代码中,有这么一段注释:
其大概的意思就是:在Vue 2.4之前的版本中,nextTick几乎都是基于microTask实现的(具体可以看文章nextTick一节),但是由于microTask的执行优先级非常高,在某些场景之下它甚至要比事件冒泡还要快,就会导致一些诡异的问题;但是如果全部都改成macroTask,对一些有重绘和动画的场景也会有性能的影响。所以最终nextTick采取的策略是默认走microTask,对于一些DOM的交互事件,如v-on绑定的事件回调处理函数的处理,会强制走macroTask。
具体做法就是,在Vue执行绑定的DOM事件时,默认会给回调的handler函数调用withMacroTask方法做一层包装,它保证整个回调函数的执行过程中,遇到数据状态的改变,这些改变而导致的视图更新(DOM更新)的任务都会被推到macroTask。
function add$1 (event, handler, once$$1, capture, passive) {
handler = withMacroTask(handler); if (once$$1) { handler = createOnceHandler(handler, event, capture); }
target$1.addEventListener(
event,
handler,
supportsPassive
? { capture: capture, passive: passive }
: capture
);
}
/**
* Wrap a function so that if any code inside triggers state change,
* the changes are queued using a Task instead of a MicroTask.
*/ function withMacroTask (fn) { return fn._withTask || (fn._withTask = function () {
useMacroTask = true;
var res = fn.apply(null, arguments);
useMacroTask = false; return res
})
}
而对于macroTask的执行,Vue优先检测是否支持原生setImmediate(高版本IE和Edge支持),不支持的话再去检测是否支持原生MessageChannel,如果还不支持的话为setTimeout(fn, 0)。
最后,写一段demo来测试一下:
<div id="example">
<span>{{test}}</span>
<button @click="handleClick">change</button>
</div> 复制代码
var vm = new Vue({
el: '#example',
data: { test: 'begin',
},
methods: {
handleClick: function() {
this.test = end;
console.log('script')
this.$nextTick(function () {
console.log('nextTick')
})
Promise.resolve().then(function () {
console.log('promise')
})
}
}
})
在Vue 2.5+中,这段代码的输出顺序是script、promise、nextTick,而Vue 2.4输出script、nextTick、promise。nextTick执行顺序的差异正好说明了上面的改变。
MessageChannel
在Vue 2.4版本以前使用的MutationObserver来模拟异步任务。而Vue 2.5版本以后,由于兼容性弃用了MutationObserver。
Vue 2.5+版本使用了MessageChannel来模拟macroTask。除了IE以外,messageChannel的兼容性还是比较可观的。
const channel = new MessageChannel() const port = channel.port2 channel.port1.onmessage = flushCallbacks macroTimerFunc = () => { port.postMessage(1) }
可见,新建一个MessageChannel对象,该对象通过port1来检测信息,port2发送信息。通过port2的主动postMessage来触发port1的onmessage事件,进而把回调函数flushCallbacks作为macroTask参与事件循环。
MessageChannel VS setTimeout
为什么要优先MessageChannel创建macroTask而不是setTimeout?
HTML5中规定setTimeout的最小时间延迟是4ms,也就是说理想环境下异步回调最快也是4ms才能触发。
Vue使用这么多函数来模拟异步任务,其目的只有一个,就是让回调异步且尽早调用。而MessageChannel的延迟明显是小于setTimeout的。对比传送门
原文链接:https://juejin.im/post/5e53f07d51882549036940fc
回复列表