新闻资讯

新闻资讯 行业动态

Vue版本升级nextTick的变化

编辑:008     时间:2020-02-25

独立文件

首先是从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
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐