写文章不容易,点个赞呗兄弟 专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】promise
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧bash
【Vue原理】NextTick - 源码版 之 宏微任务的抉择 app
nextTick 已经写了三篇文章啦,这是最后一篇源码版,没看过的童鞋能够看看白话版简单了解下拉异步
【Vue原理】NextTick - 白话版 简单了解下NextTick学习
在前面的文章 NextTick-源码版之独立自身 中ui
埋下过两个问题this
一、Vue 在哪里使用到了 宏任务和 微任务
二、Vue 为何须要 宏任务 和微任务
复制代码
今天的任务就是解决这两个问题!!!spa
在这里,你们确定必须必定要了解了 宏任务和 微任务的哈,这两个东西不赘述了线程
首先,第一个问题就是宏微任务的使用场景场景3d
一、Vue 通常状况下使用的是微任务
二、在绑定DOM 事件的时候,会使用宏任务。
这么讲,有点笼统,准确地说,应该是
事件回调执行过程当中,在JS 主线程为空以后,异步代码执行以前,全部经过 nextTick 注册的异步代码都是用宏任务。
来看看绑定DOM 事件的源码
经过 addEventListener 给 DOM 绑定事件
function add$1(event, handler) {
handler = withMacroTask(handler);
target$1.addEventListener(event, handler);
}
function withMacroTask(fn) {
return fn._withTask || (fn._withTask = function() {
useMacroTask = true;
var res = fn.apply(null, arguments);
useMacroTask = false;
return res
})
}
复制代码
你看到了,把原先DOM 事件的回调包装了一遍,而后经过设置 useMacroTask 来控制注册宏任务
useMacroTask 没见过,在 nextTick 独立流程中已经讲过了的
在调用 nextTick 的时候,正是经过这个变量来控制,这次异步代码注册的任务类型
Vue.nextTick =function (cb, ctx) {
callbacks.push(function() {
cb && cb.call(ctx);
});
if (!pending) {
pending = true;
if (useMacroTask) {
macroTimerFunc();
} else {
microTimerFunc();
}
}
}
复制代码
好多,如今咱们来解决第二个问题!
为何要特意在事件回调执行期间 使用宏任务啊,想了好很久啊,才脑抽想到去看了下 Vue 的注释
原本 Vue 是历来都使用微任务的,由于微任务的优先级比较高,执行比较快。可是同时也是由于这样致使了一个问题
在连续事件发生的期间,微任务就已经执行了
事件回调执行完成以后,会立刻执行微任务
那么连续多个事件回调同时执行,就会致使连续屡次执行微任务
若是连续多个事件回调中,都有修改数据,以下
this.state = xxxxx
那么很明显,会致使页面频繁的更新,这显然不是咱们想要的结果
那就是冒泡!
咱们来现场演示一下微任务下的冒泡事件
<div style="height:100px;width:100px;background:red">
<div style="height:60px;width:60x;background:black">
<div style="height:30px;width:30px;background:blue">
</div>
</div>
</div>
复制代码
div1.onclick = function() {
console.log("div1");
Promise.resolve().then(() = >{
console.log("promise1")
})
}
div2.onclick = function() {
console.log("div2");
Promise.resolve().then(() = >{
console.log("promise2")
})
}
div3.onclick = function() {
console.log("div3");
Promise.resolve().then(() = >{
console.log("promise3")
})
}
复制代码
看到了吗,promise 在一个事件回调结束以后立刻就调用了
若是在 Vue 中的事件回调中修改了数据 this.state = xxxxx
而后数据一更改,就会注册微任务用于响应更新,而后事件结束以后,立刻执行微任务
若是三个事件回调都有修改数据,那么就会注册三次,执行三次,就会更新三次
尤大想到了一个方法,就是在事件回调执行时,注册的是宏任务
宏任务并不会在事件结束以后立刻调用
只会在连续事件结束以后,才调用,这就是咱们想要的
因此你才能看到 使用 useMacroTask 来控制注册的任务类型
如今我把上面的例子中的 promise 换成 setTimeout,从新点击一下
在 【Vue 2.6】 中,咱们已经看不到 useMacroTask 的身影了,为何?
由于 Vue 又所有使用微任务了........ 天道轮回.....
(其实并非所有是微任务,兼容写法最后是 setTimeout)
好吧,尤大想到了另外一个办法来解决冒泡的问题
就是判断当时的 事件 target,来判断是否执行事件回调
也就间接解决了这个问题,看看新的绑定事件的源码
function add$1(name, handler) {
handler = function(e) {
if (
e.target === e.currentTarget ||
e.target.ownerDocument !== document
) {
return handler.apply(this, arguments)
}
};
target$1.addEventListener(name, handler);
}
复制代码
经过判断 target 就解决了冒泡,可是这样就不能用冒泡了好像??
也不知道有没有什么坏处,若是有的话,后面尤大确定会更新的