解读背景
- 在检查vue源代码时,nextTick方法使用浏览器的p事件作为loopp事件。同步更新。
- 在公司面试时,我最喜欢的笔试题是JavaScript工作机制、Promise/A+♽以及其他问题❙♽❙❙线程。
- 学习遵循Tick原则,帮助定位BUG并使用
V。
什么是事件循环
我们先看图(来自大佬先生)
- 首先完成同步阻塞任务。同步任务会等待上一个任务完成,然后再执行下一个任务。同步任务执行完毕后,再执行异步任务。当遇到异步任务时,将该异步任务的回调函数注册到异步任务队列中。 。注意,如果主线程中没有同步任务,则直接调用异步任务的微任务。
- 运行宏任务,所有检测到的微任务将被添加到微任务队列中。
- 开始完成微任务序列。一个宏任务完成后,会执行微任务队列,直到所有微任务队列完成且微任务队列为空。
- 完成宏任务。如果宏任务运行时存在微任务,则将微任务添加到微任务队列中。完成一个宏任务后,再完成一个微任务,直到微任务队列中的所有微任务都完成。
- 继续运行宏任务序列。
重复2、3、4、5...,直到宏任务和微任务都为空。
$nextTickin
实现原理从字面上理解,下一个下一个ticktick(时钟)来自于定时器的周期性中断(输出脉冲和中断)。 ,又称为“时钟滴答声”),nextTick顾名思义,就是时钟的下一个滴答声。
看源码,在Vue 2.x版本中nextTicku是目录❙next-tick.js ,我们看到 nextTick 的重要性。虽然它只有200多行长,但创建一个单独的文件来维护它非常重要。
接下来,我们来看看整个文件。
- 声明了三个全局变量,callbacks: [],await:Boolean,timerFunc:undefined。
- 声明函数 flushCallbacks。
- 一组**if,else if**判断。
- 抛出函数 nextTick。
nextTick函数
- 声明局部变量 _resolve 。
- 将所有回调操作放入callback并将所有callback保存为堆栈。
- 当expect为false时,执行定时器功能。
- 当回调时,返回Promise回调,可以被♾t接收。
timerFunc函数
我们开始讨论将timerFunc 作为全局变量。现在调用timerFunc。 timerFunc什么时候被定义为一个函数,该函数的执行代码是什么?
我们可以看到这段判断代码一共有四个分支,并且timerFunc在四个分支中有不同的赋值。我们先看第一个分支。
承诺分行
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
// In problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer.
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
}
- 判断环境是否支持Promise,以及Promise是否是原创的。
- 使用 Promise
异步调用 flushCallbacks。
- 当执行环境为iPhone♂等时,使用setTimeout调用always,always iOS 在某些异常网页视图中 promise过期后没有任务队列更新,因此强制执行setTimeoutreshsh。
MutationObserver 分支
else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// Use MutationObserver where native Promise is not available,
// e.g. PhantomJS, iOS7, Android 4.4
// (#6466 MutationObserver is unreliable in IE11)
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
isUsingMicroTask = true
}
- 评估是否可以使用非IE浏览器和HTML5新功能MutationObserver。
- 实例化一个 MutationObserver 对象。此项主要是跟踪浏览器的变化观察设置DOM节点变化时会自动触发回调。
- 将timerFunc
定义为当
节点、♶
D 冲洗时更改DOM节点的方法。 (其实这里需要使用MutationObserver属性来进行异步操作)function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
设置直属分支
else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// Fallback to setImmediate.
// Technically it leverages the (macro) task queue,
// but it is still a better choice than setTimeout.
timerFunc = () => {
setImmediate(flushCallbacks)
}
}
- 确定 setImmediate♂ 是否存在,setImmediate♂ 是较新版本 较新版本 仅支持 + edge。
- 如果启用,请跳过 flushCallbacks 运行 setImmediate♂ 。
setTimeout分支
else {
// Fallback to setTimeout.
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
- 当以上所有异步分支api♂均不支持时,请使用宏♶♶♶
setTimeout运行刷新回调。function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
运行较低版本
我们可以注意到,给timerFunc赋值是一个递减的过程。为什么呢,因为Vue在执行过程中执行环境是不一样的,所以它要适应环境。
这张图可以帮助我们更清楚地理解底层流程。
冲洗功能
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
循环遍历,按照队列原则“先进先出”,逐一执行所有回调。
总结
仅此而已。 nextTick♂的原理是利用Event♂
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。