前端筆記-vue v2.6.10原始碼註釋-nextTick

superior發表於2019-04-29

程式碼順序:

1、Vue例項初始化

2、renderMixin中設定$nextTickAPI

3、nextTick原始碼

以下為nextTick原始碼的註釋(這裡只將原始碼中的註釋刪掉了,加了中文註釋)

import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'

// noop 為空函式
// handleError 為錯誤處理
// isIE 為判斷是否為IE環境
// isIOS 為判斷是否為iOS環境
// isNative 為判斷傳入引數是否為內建函式

// 是否使用微任務標誌(預設為falseexport 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
    })
  }
}

複製程式碼

相關文章