写文章不容易,点个赞呗兄弟 专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】vue
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧node
【Vue原理】NextTick - 源码版 之 独立自身 数组
好的,今天到了 nextTick 的环节,以前我看的版本是 2.5.17,而后瞄了一眼 2.6 的,发现对于 nextTick 修改了 少部份内容,可是不太大,因此就一块儿记录下来promise
(若是改太多,就懒得看了.....反正了解一个思想以及实现思路就好了)异步
nextTick 是一个在 Vue 中比较独立的东西,能够直接拿出来为你的项目服务函数
nextTick 涉及的点,就下面这些post
一、任务队列callbacks 二、任务队列执行函数 flushCallbacks 三、控制(宏任务,微任务)注册标志位 pending 四、宏任务,微任务
没看懂?不要紧,后面会慢慢说学习
这篇先讲 nextTick 自身,下篇再讲 nextTick 和 Vue 的关联this
接下来就是一个个去详细记录了3d
这个知识点,很重要,也不算太简单,在网上也能找到不少很好的讲解,好比下面这篇文章,在这里不会特别解释这两个,毕竟主题不是这个
http://www.javashuo.com/article/p-cmtsxzha-h.html
宏微任务的下面总结也是我的理解,有错尽管骂我
那么这里就先记录一下相关的结论
一、宏任务和微任务都是异步 二、宏任务和微任务会被注册到两个不一样的队列中 三、宏任务队列不是一次性清空执行,而是执行一个宏任务时, 而后去清空执行一列微任务队列
接着再执行下一个宏任务.....循环往复,直到全部队列都为空
好比 一个 setTimeout 就是一个宏任务,两个 setTimeout 就是两个宏任务
好比如今,宏任务队列中有两个 setTimeout,微任务队列中有两个 Promise
假设如今正在执行第一个宏任务 setTimeout,执行完以后,会开始清空执行 微任务队列
因而开始执行了两个Promise
结束以后,接着执行 另外一个宏任务, setTimeout
之前我觉得是 宏任务队列执行完,再执行微任务队列,发现不是,很受伤,都是了解 nextTick 源码让我有机会从新了解了一遍 这个知识点
setTimeout
setInterval
setImmediate
script
MessageChannel
Promise
MutationObserver
Object.observe(废弃)
process.nextTick(node)
如下谈的是 版本 2.5.17 的,在 2.6 中,去掉宏任务了
在这里先埋下两个问题
一、Vue为何须要宏任务和 微任务 二、Vue在哪里使用到了宏任务和微任务
这两个问题会记录在另一篇文章
Vue 中有两个函数,macroTimerFunc 用于注册宏任务,microTimerFunc 用于注册微任务
以适用于不一样的场景,下面就是这两个函数的源码
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回调,从另外一个端口接收传过来的数据
很少说了,看下一个微任务
if(若是promise存在) { varp =Promise.resolve(); microTimerFunc =function(){ p.then(flushCallbacks); }; }else{ microTimerFunc = macroTimerFunc; }
上面的宏微任务 函数都 出现了一个 flushCallbacks 的东西,下面会有
vue 本身维护了一个任务队列去配合 宏微任务使用,目的无非是几样
一、减小宏微任务的注册。尽可能把全部异步代码放在一个 宏微任务中,减小消耗
二、加快异步代码的执行。咱们知道,若是一个异步代码就注册一个宏微任务的话,那么执行彻底部异步代码确定慢不少
三、避免频繁地更新。Vue 中就算咱们一次性修改屡次数据,页面仍是只会更新一次。就是由于这样,避免屡次修改数据致使的屡次频繁更新页面,让屡次修改只用更新最后一次
下面就来讲一下Vue 相关的实现
callbacks 是一个数组,用于存放各类异步函数。好比
this.$nextTick(()=>{ console.log(1111) })
就会把你设置的这个回调,放到 callbacks 数组中
callbacks.push(()=>{ console.log(1111) })
既然 callbacks 是存放异步回调的,那么确定有一个方法,是遍历 callbacks ,而后逐个执行其中存放的函数
没错,这个方法就是 flushCallbacks
方法灰常简单啊,你们确定能看得懂啊
一、复制一遍 callbacks
二、把 原来 callbacks 清空
三、遍历 复制的 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 函数闪亮登场!!!
Vue.nextTick =function(cb, ctx){ callbacks.push(function(){ cb && cb.call(ctx); }); if(!pending) { pending =true; if(useMacroTask) { macroTimerFunc(); }else{ microTimerFunc(); } } }
经过判断 pending 来肯定是否须要注册宏微任务
当第一次注册的时候,把 pending 设置为 true,表示任务队列已经在开始了,同一时期内无需注册了
而后在 任务队列 执行完毕以后,再把 pending 设置为 false(在 flushCallbacks 中)
你能够看到,就是在这里进行存放 异步函数,还特意【包装】了一遍,为了绑定一个上下文对象
Vue 怎么控制注册宏任务仍是微任务呢?
没错,就是这个鬼东西了,设置为 true 时注册宏任务,设置为false 注册微任务
“在 2.6 版本中,已经不存在这个鬼东西,所有使用了微任务注册”
在 注册 DOM 事件的时候用到,当事件回调执行的过程当中,全部的异步代码都使用宏任务
你问为何?内容太多,会有专篇分析
而后,关于 macroTimerFunc 和 microTimerFunc 上文已经讲过啦,能够回去看看