首頁 web前端 js教程 Vue.nextTick 的實作方法淺析

Vue.nextTick 的實作方法淺析

Jan 03, 2018 am 11:17 AM
實現

本文主要介紹了Vue.nextTick 的實作方法,這是一篇繼event loop和MicroTask 後的vue.nextTick API實作的原始碼解析。小編覺得蠻不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧,希望能幫助大家。

預熱,寫一個sleep函數


function sleep (ms) {
 return new Promise(resolve => setTimeout(resolve, ms)
}
async function oneTick (ms) {
 console.log('start')
 await sleep(ms)
 console.log('end')
}
oneTick(3000)
登入後複製

解釋下sleep函數

async 函數進行await PromiseFn()時函數執行是暫停的,我們也知道現在這個PromiseFn是在microTask內執行。當microTask沒執行完畢時,後面的macroTask是不會執行的,我們也就透過microTask在event loop的特性實作了一個sleep函數,阻止了console.log的執行

#流程

1執行console.log('start')
2執行await 執行暫停,等待await函數後的PromiseFn在microTask執行完畢
3在sleep函數內,延遲ms回傳
4返回resolve後執行console.log('end')

nextTick API

vue中nextTick的使用方法


vue.nextTick(() => {
 // todo...
})
登入後複製

了解用法後看一下源碼


const nextTick = (function () {
 const callbacks = []
 let pending = false
 let timerFunc // 定时函数

 function nextTickHandler () {
  pending = false
  const copies = callbacks.slice(0) // 复制
  callbacks.length = 0 // 清空
  for (let i = 0; i < copies.length; i++) {
   copies[i]() // 逐个执行
  }
 }

 if (typeof Promise !== &#39;undefined&#39; && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError) // 重点
  }
 } else if (&#39;!isIE MutationObserver&#39;) {
  var counter = 1
  var observer = new MutationObserver(nextTickHandler) // 重点
  var textNode = document.createTextNode(string(conter))

  observer.observe(textNode, {
   characterData: true
  })
  timerFunc = () => {
   counter = (counter + 1) % 2
   textNode.data = String(counter)
  }
 } else {
  timerFunc = () => {
   setTimeout(nextTickHandler, 0) // 重点
  }
 }


 return function queueNextTick (cb, ctx) { // api的使用方式
  let _resolve
  callbacks.push(() => {
   if (cb) {
    try {
     cb.call(ctx)
    } catch (e) {
     err
    }
   } else if (_resolve) {
    _resolve(ctx)
   }
  })
  if (!pending) {
   pending = true
   timerFunc()
  }
  if (!cb && typeof Promise !== &#39;undefined&#39;) {
   return new Promise((resolve, reject) => {
    _resolve =resolve
   })
  }
 }
})() // 自执行函数
登入後複製

大致看一下原始碼可以了解到nextTick api是一個自執行函數

現在是自執行函數,直接看它的return類型,return function queueNextTick (cb, ctx) {...}


return function queueNextTick (cb, ctx) { // api的使用方式
  let _resolve
  callbacks.push(() => {
   if (cb) {
    try {
     cb.call(ctx)
    } catch (e) {
     err
    }
   } else if (_resolve) {
    _resolve(ctx)
   }
  })
  if (!pending) {
   pending = true
   timerFunc()
  }
  if (!cb && typeof Promise !== &#39;undefined&#39;) {
   return new Promise((resolve, reject) => {
    _resolve =resolve
   })
  }
 }
登入後複製

只專注於主流程queueNextTick函數把我們傳入的() => { // todo... } 推入了callbacks內


 if (typeof Promise !== &#39;undefined&#39; && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError) // 重点
  }
 } else if (&#39;!isIE MutationObserver&#39;) {
  var counter = 1
  var observer = new MutationObserver(nextTickHandler) // 重点
  var textNode = document.createTextNode(string(conter))

  observer.observe(textNode, {
   characterData: true
  })
  timerFunc = () => {
   counter = (counter + 1) % 2
   textNode.data = String(counter)
  }
 } else {
  timerFunc = () => {
   setTimeout(nextTickHandler, 0) // 重点
  }
 }
登入後複製

這段我們可以看到標註的三個點表示在不同瀏覽器環境下使用Promise, MutationObserver或setTimeout(fn, 0) 來執行nextTickHandler


function nextTickHandler () {
  pending = false
  const copies = callbacks.slice(0) // 复制
  callbacks.length = 0 // 清空
  for (let i = 0; i < copies.length; i++) {
   copies[i]() // 逐个执行
  }
 }
登入後複製

nextTickHandler就是把我們之前放入callbacks的() => { // todo... } 在目前tasks內執行。

寫一個簡單的nextTick

原始碼可能比較繞,我們自己寫一段簡單的nextTick


const simpleNextTick = (function () {
 let callbacks = []
 let timerFunc

 return function queueNextTick (cb) {
  callbacks.push(() => { // 给callbacks 推入cb()
   cb()
  })

  timerFunc = () => {
   return Promise.resolve().then(() => {
    const fn = callbacks.shift()
    fn()
   })
  }
  timerFunc() // 执行timerFunc,返回到是一个Promise
 }
})()

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})
登入後複製

我們可以從這裡看出nextTick的原理就是回傳一個Promise,而我們todo的程式碼在這個Promise中執行,現在我們還可以繼續簡化


const simpleNextTick = (function () {
 return function queueNextTick (cb) {
  timerFunc = () => {
   return Promise.resolve().then(() => {
    cb()
   })
  }
  timerFunc()
 }
})()

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})
登入後複製

直接寫成這樣。


const simpleNextTick = function queueNextTick (cb) {
  timerFunc = () => {
   return Promise.resolve().then(() => {
    cb()
   })
  }
  timerFunc()
 }

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})
登入後複製

這次我們把自執行函數也簡化掉


const simpleNextTick = function queueNextTick (cb) {
   return Promise.resolve().then(cb)
 }

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})
登入後複製

現在我們直接簡化到最後,現在發現nextTick最核心的內容就是Promise,一個microtask。

現在我們回到vue的nextTick API官方範例


<p id="example">{{message}}</p>
var vm = new Vue({
 el: &#39;#example&#39;,
 data: {
  message: &#39;123&#39;
 }
})
vm.message = &#39;new message&#39; // 更改数据
vm.$el.textContent === &#39;new message&#39; // false
Vue.nextTick(function () {
 vm.$el.textContent === &#39;new message&#39; // true
})
登入後複製

原來在vue內資料的更新後dom更新是要在下一個事件循環後執行的。
nextTick的使用原則主要是解決單一事件更新資料後立即操作dom的場景。

既然我們知道了nextTick核心是利用microTasks,那麼我們把簡化過的nextTick和開頭的sleep函數對照一下。


const simpleNextTick = function queueNextTick (cb) {
   return Promise.resolve().then(cb)
 }

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;) // 也可以换成ajax请求
})
登入後複製


function sleep (ms) {
 return new Promise(resolve => setTimeout(resolve, ms) // 也可以换成ajax请求
}
async function oneTick (ms) {
 console.log(&#39;start&#39;)
 await sleep(ms)
 console.log(&#39;end&#39;)
}
oneTick(3000)
登入後複製

我們看出nextTick和我麼寫的oneTick的執行結果是那麼的相似。差異只在於nextTick是把callback包裹一個Promise回傳並執行,而oneTick是用await執行一個Promise函數,而這個Promise有自己包裹的webapi函數。

那在用ajax請求的時候我們是不是直接這樣使用axios可以返回Promise的庫


async function getData () {
  const data = await axios.get(url)
  // 操作data的数据来改变dom
  return data
}
登入後複製

這樣也可以達到同nextTick同樣的作用

最後我們也可以從原始碼中看出,當瀏覽器環境不支援Promise時可以使用MutationObserver或setTimeout(cb, 0) 來達到相同的效果。但最終的核心是microTask

相關推薦:

Node.js中的process.nextTick使用實例

node .js中的定時器nextTick()和setImmediate()區別分析_node.js

Node.js中的process.nextTick使用實例_node.js

#

以上是Vue.nextTick 的實作方法淺析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

華為手機如何實現雙微信登入? 華為手機如何實現雙微信登入? Mar 24, 2024 am 11:27 AM

華為手機如何實現雙微信登入?隨著社群媒體的興起,微信已成為人們日常生活中不可或缺的溝通工具之一。然而,許多人可能會遇到一個問題:在同一部手機上同時登入多個微信帳號。對於華為手機用戶來說,實現雙微信登入並不困難,本文將介紹華為手機如何實現雙微信登入的方法。首先,華為手機自帶的EMUI系統提供了一個很方便的功能-應用程式雙開。透過應用程式雙開功能,用戶可以在手機上同

使用Java編寫程式碼實作愛心動畫 使用Java編寫程式碼實作愛心動畫 Dec 23, 2023 pm 12:09 PM

透過Java程式碼實現愛心動畫效果在程式設計領域中,動畫效果是非常常見且受歡迎的。可以透過Java程式碼實現各種各樣的動畫效果,其中之一就是愛心動畫效果。本文將介紹如何使用Java程式碼來實現此效果,並給出具體的程式碼範例。實現愛心動畫效果的關鍵在於繪製心形圖案,並透過改變心形的位置和顏色來實現動畫效果。下面是一個簡單範例的程式碼:importjavax.swing.

PHP程式設計指南:實作斐波那契數列的方法 PHP程式設計指南:實作斐波那契數列的方法 Mar 20, 2024 pm 04:54 PM

程式語言PHP是一種用於Web開發的強大工具,能夠支援多種不同的程式設計邏輯和演算法。其中,實作斐波那契數列是一個常見且經典的程式設計問題。在這篇文章中,將介紹如何使用PHP程式語言來實作斐波那契數列的方法,並附上具體的程式碼範例。斐波那契數列是一個數學上的序列,其定義如下:數列的第一個和第二個元素為1,從第三個元素開始,每個元素的值等於前兩個元素的和。數列的前幾元

如何在華為手機上實現微信分身功能 如何在華為手機上實現微信分身功能 Mar 24, 2024 pm 06:03 PM

如何在華為手機上實現微信分身功能隨著社群軟體的普及和人們對隱私安全的日益重視,微信分身功能逐漸成為人們關注的焦點。微信分身功能可以幫助使用者在同一台手機上同時登入多個微信帳號,方便管理和使用。在華為手機上實現微信分身功能並不困難,只需要按照以下步驟操作即可。第一步:確保手機系統版本和微信版本符合要求首先,確保你的華為手機系統版本已更新至最新版本,以及微信App

開發建議:如何利用ThinkPHP框架實現非同步任務 開發建議:如何利用ThinkPHP框架實現非同步任務 Nov 22, 2023 pm 12:01 PM

《開發建議:如何利用ThinkPHP框架實現非同步任務》隨著網路技術的快速發展,Web應用程式對於處理大量並發請求和複雜業務邏輯的需求也越來越高。為了提高系統的效能和使用者體驗,開發人員常常會考慮利用非同步任務來執行一些耗時操作,例如發送郵件、處理文件上傳、產生報表等。在PHP領域,ThinkPHP框架作為一個流行的開發框架,提供了一些便捷的方式來實現非同步任務。

掌握Golang如何實現遊戲開發的可能性 掌握Golang如何實現遊戲開發的可能性 Mar 16, 2024 pm 12:57 PM

在現今的軟體開發領域中,Golang(Go語言)作為一種高效、簡潔、並發性強的程式語言,越來越受到開發者的青睞。其豐富的標準庫和高效的並發特性使它成為遊戲開發領域的一個備受關注的選擇。本文將探討如何利用Golang來實現遊戲開發,並透過具體的程式碼範例來展示其強大的可能性。 1.Golang在遊戲開發中的優勢作為靜態類型語言,Golang正在建構大型遊戲系統

如何在Golang中實現精確除法運算 如何在Golang中實現精確除法運算 Feb 20, 2024 pm 10:51 PM

在Golang中實現精確除法運算是一個常見的需求,特別是在涉及金融計算或其它需要高精度計算的場景中。 Golang的內建的除法運算子「/」是針對浮點數計算的,並且有時會出現精度遺失的問題。為了解決這個問題,我們可以藉助第三方函式庫或自訂函數來實現精確除法運算。一種常見的方法是使用math/big套件中的Rat類型,它提供了分數的表示形式,可以用來實現精確的除法運算

PHP遊戲需求實作指南 PHP遊戲需求實作指南 Mar 11, 2024 am 08:45 AM

PHP遊戲需求實現指南隨著網路的普及和發展,網頁遊戲的市場也越來越火爆。許多開發者希望利用PHP語言來開發自己的網頁遊戲,而實現遊戲需求是其中一個關鍵步驟。本文將介紹如何利用PHP語言來實現常見的遊戲需求,並提供具體的程式碼範例。 1.創造遊戲角色在網頁遊戲中,遊戲角色是非常重要的元素。我們需要定義遊戲角色的屬性,例如姓名、等級、經驗值等,並提供方法來操作這些

See all articles