资讯专栏INFORMATION COLUMN

【Vue原理】NextTick - 源码版 之 独立自身

刘东 / 1090人阅读

摘要:尽量把所有异步代码放在一个宏微任务中,减少消耗加快异步代码的执行。我们知道,如果一个异步代码就注册一个宏微任务的话,那么执行完全部异步代码肯定慢很多避免频繁地更新。中就算我们一次性修改多次数据,页面还是只会更新一次。

写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue版本 【2.5.17】

如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧

【Vue原理】NextTick - 源码版 之 独立自身

好的,今天到了 nextTick 的环节,之前我看的版本是 2.5.17,然后瞄了一眼 2.6 的,发现对于 nextTick 修改了 少部分内容,但是不太大,所以就一起记录下来

(如果改太多,就懒得看了.....反正了解一个思想以及实现思路就行了)

nextTick 是一个在 Vue 中比较独立的东西,可以直接拿出来为你的项目服务

nextTick 涉及的点,就下面这些

1、任务队列callbacks
2、任务队列执行函数 flushCallbacks
3、控制(宏任务,微任务)注册标志位 pending
4、宏任务,微任务

没看懂?没关系,后面会慢慢说

这篇先讲 nextTick 自身,下篇再讲 nextTick 和 Vue 的关联

接下来就是一个个去详细记录了

宏任务,微任务

这个知识点,很重要,也不算太简单,在网上也能找到很多很好的讲解,比如下面这篇文章,在这里不会特别解释这两个,毕竟主题不是这个

https://juejin.im/post/59e85e...

宏微任务的下面总结也是个人理解,有错尽管骂我

那么这里就先记录一下相关的结论

1、宏任务和微任务都是异步
2、宏任务和微任务会被注册到两个不同的队列中
3、宏任务队列不是一次性清空执行,而是执行一个宏任务时,
然后去清空执行一列微任务队列

接着再执行下一个宏任务.....循环往复,直到所有队列都为空

什么是一个宏任务

比如 一个 setTimeout 就是一个宏任务,两个 setTimeout 就是两个宏任务

例子说明执行顺序

比如现在,宏任务队列中有两个 setTimeout,微任务队列中有两个 Promise

假设现在正在执行第一个宏任务 setTimeout,执行完之后,会开始清空执行 微任务队列

于是开始执行了两个Promise

结束之后,接着执行 另一个宏任务, setTimeout

以前我以为是 宏任务队列执行完,再执行微任务队列,发现不是,很受伤,都是了解 nextTick 源码让我有机会重新了解了一遍 这个知识点

常见宏任务

setTimeout

setInterval

setImmediate

script

MessageChannel

常见微任务

Promise

MutationObserver

Object.observe(废弃)

process.nextTick(node)

Vue 中的宏任务 和 微任务 源码

以下谈的是 版本 2.5.17 的,在 2.6 中,去掉宏任务了

在这里先埋下两个问题

1、Vue为什么需要宏任务和 微任务
2、Vue在哪里使用到了宏任务和微任务

这两个问题会记录在另外一篇文章

Vue 中有两个函数,macroTimerFunc 用于注册宏任务,microTimerFunc 用于注册微任务

以适用于不同的场景,下面就是这两个函数的源码

1、macroTimerFunc
if(如果setImmediate存在) {
    macroTimerFunc =function(){
        setImmediate(flushCallbacks);
    };

}

elseif(如果MessageChannel存在) {    

   varchannel =newMessageChannel();    

    varport = channel.port2;

   channel.port1.onmessage = flushCallbacks;
    macroTimerFunc =function(){
        port.postMessage(1);
    };

}

else{

   macroTimerFunc =function(){
        setTimeout(flushCallbacks,0);
    };
}

没啥好说的,最多记录一下 MessageChannel,更多内容就自己查啦

MessageChannel

简单来说,MessageChannel 用于创建了一个通信的管道,这个管道有两个端口

每个端口都可以通过postMessage发送数据

一个端口绑定onmessage回调,从另一个端口接收传过来的数据

不多说了,看下一个微任务

2、microTimerFunc
if(如果promise存在) {    

    varp =Promise.resolve();

   microTimerFunc =function(){
        p.then(flushCallbacks);
    };
}else{
    microTimerFunc = macroTimerFunc;
}

上面的宏微任务 函数都 出现了一个 flushCallbacks 的东西,下面会有

Vue 的任务队列

vue 自己维护了一个任务队列去配合 宏微任务使用,目的无非是几样

1、减少宏微任务的注册。尽量把所有异步代码放在一个 宏微任务中,减少消耗

2、加快异步代码的执行。我们知道,如果一个异步代码就注册一个宏微任务的话,那么执行完全部异步代码肯定慢很多

3、避免频繁地更新。Vue 中就算我们一次性修改多次数据,页面还是只会更新一次。就是因为这样,避免多次修改数据导致的多次频繁更新页面,让多次修改只用更新最后一次

下面就来说一下Vue 相关的实现

1、callbacks

callbacks 是一个数组,用于存放各种异步函数。比如

this.$nextTick(()=>{    

    console.log(1111)

})

就会把你设置的这个回调,放到 callbacks 数组中

callbacks.push(()=>{    
    console.log(1111)
})

既然 callbacks 是存放异步回调的,那么肯定有一个方法,是遍历 callbacks ,然后逐个执行其中存放的函数

没错,这个方法就是 flushCallbacks

2、flushCallbacks

方法灰常简单啊,大家肯定能看得懂啊

1、复制一遍 callbacks

2、把 原来 callbacks 清空

3、遍历 复制的 callbacks ,然后逐个执行

var callbacks = [];

var pending =false;


functionflushCallbacks(){

   pending =false;    

    varcopies = callbacks.slice(0);

   callbacks.length =0;    

    for(vari =0; i < copies.length; i++) {

       copies[i]();
    }
}

这个方法是 直接传给 上面设置的 宏任务函数 和 微任务函数的额

也就是说,宏任务和 微任务 的回调,都是执行这个 flushCallbacks

setTimeout(flushCallbacks)

嘿,我们之前有讲过,Vue 会控制当时执行栈的所有异步代码只注册一个 宏微任务

那么是怎么控制的呢?

还有还有,是怎么把 异步函数 存放到 callbacks 中的呢?

下面就需要请出我们的猪脚,nextTick 函数闪亮登场!!!

3、NextTick
Vue.nextTick =function(cb, ctx){

    callbacks.push(function(){
        cb && cb.call(ctx);
   });    

    if(!pending) {

       pending =true;        

        if(useMacroTask) {

           macroTimerFunc();
        }else{
            microTimerFunc();
        }
    }
}
1、pending

通过判断 pending 来确定是否需要注册宏微任务

当第一次注册的时候,把 pending 设置为 true,表示任务队列已经在开始了,同一时期内无需注册了

然后在 任务队列 执行完毕之后,再把 pending 设置为 false(在 flushCallbacks 中)

2、callbacks

你可以看到,就是在这里进行存放 异步函数,还特地【包装】了一遍,为了绑定一个上下文对象

3、useMacroTask

Vue 怎么控制注册宏任务还是微任务呢?

没错,就是这个鬼东西了,设置为 true 时注册宏任务,设置为false 注册微任务

“在 2.6 版本中,已经不存在这个鬼东西,全部使用了微任务注册”

这个东西,在哪里用过啊?

在 注册 DOM 事件的时候用到,当事件回调执行的过程中,所有的异步代码都使用宏任务

你问为什么?内容太多,会有专篇分析

然后,关于 macroTimerFunc 和 microTimerFunc 上文已经讲过啦,可以回去看看

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/110260.html

相关文章

  • Vue原理NextTick - 源码 服务Vue

    写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】NextTick - 源码版 之 服务Vue 初次看的兄弟可以先看 【Vue原理】NextTick - 白话版 简单了解下...

    Acceml 评论0 收藏0
  • Vue原理NextTick - 源码 宏微任务的抉择

    摘要:这么讲,有点笼统,准确地说,应该是事件回调执行过程中,在主线程为空之后,异步代码执行之前,所有通过注册的异步代码都是用宏任务。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【...

    raise_yang 评论0 收藏0
  • Vue原理NextTick - 白话

    摘要:通常会做很多判断来选择存在的类型,比如判断等是否存在,而选择他为微任务类型但是可能宏微任务最后都是,因为他是保守兼容处理。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【V...

    zeyu 评论0 收藏0
  • 阿里校招前端面经

    摘要:的回调函数执行的优先级要高于,属于观察者。的回调函数保存在一个数组中,会将异步回调放到当前帧的末尾回调之前,如果过多,会导致回调不断延后最后堆积太多。 阿里一面是电话面,问得不多,但是挺有深度。面试官一开始就说,看了你的项目,觉得你基础挺好的,那我就不问基础了。然后全程就真的没有问一个基础问题。。 1.说说你做的那个网页版手机QQ项目的难点。 我首先想到了滚动条位置无法还原的问题,也就...

    ccj659 评论0 收藏0
  • 前方来报,八月最新资讯--关于vue2&3的最佳文章推荐

    摘要:哪吒别人的看法都是狗屁,你是谁只有你自己说了才算,这是爹教我的道理。哪吒去他个鸟命我命由我,不由天是魔是仙,我自己决定哪吒白白搭上一条人命,你傻不傻敖丙不傻谁和你做朋友太乙真人人是否能够改变命运,我不晓得。我只晓得,不认命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出处 查看github最新的Vue...

    izhuhaodev 评论0 收藏0

发表评论

0条评论

刘东

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<