【Vue原理】NextTick - 源码版 之 宏微任务的抉择

写文章不容易,点个赞呗兄弟 专一 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 就解决了冒泡,可是这样就不能用冒泡了好像??

也不知道有没有什么坏处,若是有的话,后面尤大确定会更新的

公众号
相关文章
相关标签/搜索