程式碼順序:
1、Vue例項初始化
以下為nextTick原始碼的註釋(這裡只將原始碼中的註釋刪掉了,加了中文註釋)
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'
// noop 為空函式
// handleError 為錯誤處理
// isIE 為判斷是否為IE環境
// isIOS 為判斷是否為iOS環境
// isNative 為判斷傳入引數是否為內建函式
// 是否使用微任務標誌(預設為false)
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是否存在(非undefined & 必須為內建函式)
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve() // 初始化一個Promise物件(fulfilled)
timerFunc = () => {
p.then(flushCallbacks)
// iOS中在一些異常的webview中,promise結束後任務佇列並沒有重新整理
// 所以強制執行setTimeout重新整理任務佇列
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true // 重置使用微任務標示為true
} else if (
!isIE &&
typeof MutationObserver !== 'undefined' &&
(
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)
) {
// 在android、iOS、PhantomJS使用MutationObserver
// MutationObserver介面提供了監視對DOM樹所做更改的能力
// 參考地址:https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
let counter = 1
// 例項化新的MutationObserver物件
// 在例項化的時候傳入flushCallbacks為變化時回撥
// MutationObserver回撥為微任務!
const observer = new MutationObserver(flushCallbacks)
// 建立一個文字節點
const textNode = document.createTextNode(String(counter))
// 對建立的文字節點監聽
// 參考地址:https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver/observe
observer.observe(textNode, {
characterData: true // 在文字節點變化時,記錄更新前的值
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter) // 更新文字節點內容
}
isUsingMicroTask = true // 重置使用微任務標示為true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// 使用setImmediate(只有IE支援,巨集任務但是優先順序比setTimeout高)
// 參考文件:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/setImmediate
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// 上述方案均不可行時使用setTimeout方法
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
// 對外暴露的nextTick方法
export function nextTick (cb?: Function, ctx?: Object) {
// cb為使用者傳入的回撥函式
// ctx為當前vue例項(this)
let _resolve
// 在回撥佇列中加入回撥函式
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx) // 執行回撥函式並繫結this
} catch (e) {
handleError(e, ctx, 'nextTick') // 異常處理
}
} else if (_resolve) {
// 如果cb無效且_resolve已經置為promise.resolve
// 則執行一次
_resolve(ctx)
}
})
if (!pending) {
// 將pending置為true並開始執行回撥佇列
pending = true
timerFunc()
}
// 當cb不存在時,將_resolve置為promise.resolve
// 並返回promise物件
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
複製程式碼