做外贸要建什么网站/搜狗搜索引擎网页
目录
- 作用
- 原理分析
- 源码
作用
在下次 DOM 更新循环结束之后执行回调函数。vue
中对DOM
的更新策略是异步的。只要监听到数据变化,vue
将会开启一个事件队列,并缓冲同一事件循环中的所有数据变化。如果同一个watcher
被触发多次,只会被推入到事件队列中一次,防止不必要的计算和DOM
操作。
原理分析
微任务和宏任务相结合,优先使用微任务。对于一些不支持原生promise
,MutationObserver
等API
的浏览器,会降级使用宏任务(setTimeout
)
1、依次判断浏览器是否支持Promise
->MutationObserver
->setImmediate
->setTimeout
。使用对应支持的API
创建一个异步执行的函数,函数名称为timerFunc
。
2、调用nextTick
的时候,首先会把回调函数放到一个队列数组中,然后添加一个异步锁,上锁,执行timerFunc
函数。等再次调用nextTick
的时候。因为有个异步锁,所以不会再次执行timerFunc
函数,这样子就可以防止回调函数被多次执行了
3、由于timerFunc
函数是异步函数,等所有同步代码都执行完毕之后,timerFunc
函数就会把队列数组中的函数进行遍历,依次执行,然后清空队列数组,同时还要对异步锁进行解锁
源码
源码位于src/core/util/next-tick.js
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'export let isUsingMicroTask = false// 回调队列
const callbacks = []
// 异步锁
let pending = falsefunction flushCallbacks () {// 解锁pending = false// 备份回调函数,防止nexttick里面又使用了nexttick,导致里面的nexttick会进入回调队列const copies = callbacks.slice(0)// 清空回调队列callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}
}// 异步函数
let timerFuncif (typeof Promise !== 'undefined' && isNative(Promise)) {// 先检查是否支持原生promiseconst p = Promise.resolve()timerFunc = () => {p.then(flushCallbacks)if (isIOS) setTimeout(noop)}isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) ||// PhantomJS and iOS 7.xMutationObserver.toString() === '[object MutationObserverConstructor]'
)) {// 然后再检查是否支持MutationObserverlet counter = 1const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode(String(counter))observer.observe(textNode, {characterData: true})timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// ie或者nodejs支持setImmediatetimerFunc = () => {setImmediate(flushCallbacks)}
} else {// Fallback to setTimeout.// 都不支持的情况下,降级使用setTimeouttimerFunc = () => {setTimeout(flushCallbacks, 0)}
}export function nextTick (cb?: Function, ctx?: Object) {let _resolve// 将回调函数推入到回调队列中callbacks.push(() => {if (cb) {try {cb.call(ctx)} catch (e) {handleError(e, ctx, 'nextTick')}} else if (_resolve) {_resolve(ctx)}})if (!pending) {// 接收到第一个回调函数的时候就上锁// 上锁,执行异步方法pending = truetimerFunc()}// $flow-disable-lineif (!cb && typeof Promise !== 'undefined') {// 没有传入回调函数的,并且支持promise的情况下,返回promise实例return new Promise(resolve => {_resolve = resolve})}
}