网络上 其实讲 nextTick 的文章五花八门,我看了也获益匪浅,但是我这段时间 看了源码之后,发现 vue 的nextTick 又有了 较大的改动
/* @flow */
/* globals MutationObserver */
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'
export let isUsingMicroTask = false
const callbacks = []
let pending = false
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
let timerFunc
// 以前 还会 分成两个函数,分别装 微任务 和 宏任务,但是这里,已经一把梭了
// 很直接的 按照顺序进行判断
// 先是 Promise
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
// 接着是 MutationObserver ,这个文后继续介绍
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
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
// 再然后是 setImmediate 这个是 IE 高级浏览器的属性
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// 最后就 轮到了 常见的 setTimeout 了
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
// 由于 js 是单线程的,在一个 轮询完成之前,所有 调用 nextTick 的函数都会被收集到
// 这个 callback 里面,然后被集中执行
callbacks.push(() => {
if (cb) {
// 这里使用 try catch 包裹,
// 也是为了防止其中一个函数 报错之后,线程奔溃,导致其他的 函数不能正常执行
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
// 有没有觉得 这个用法很熟悉?
// 没错了,就是 我们常常看到的 节流,就是throttle 的使用方法
// 倍感亲切啊,有没有,学到的东西被用上了!!
if (!pending) {
pending = true
timerFunc()
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
上面的代码相信 有基础的人都能很轻易地看懂
但是这里最大的收获 却是 MutationObserver
所以说凭什么人家能写出这么好的东西,我却只能望其项背呢?
先看兼容性
1、除了老大哥 IE 的兼容性不好,竟然完美地兼容了大部分浏览器 !!!
2、这个属性是一个 微任务
3、关于具体的用法,这里也不复制粘贴了,贴一个链接 developer MutationObserver 监听DOM树变化 这篇文章写的很好
4、再贴一遍 nextTick 里面相关的写法
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)
}