10 分鐘瞭解 Vue nextTick 獲取更新後 DOM 的原理

Vue nexTick

使用過 Vue 的小夥伴們都知道,Vue 裏的 nextTick 可以獲取到更新後的 DOM, 今天我就來講解下 nextTick 裏面究竟做了什麼?

開始講解前,我們需要知道了解一個概念,那就是 Event Loop

Event Loop

Event Loop 翻譯過來就是事件循環, 一個 Event Loop 會包括一個或多個 task 隊列,持續線程會從隊列中取出最早進入隊列的任務進行執行,被取出的任務就叫做 macroTask(宏任務), 每個 macroTask 都有一個任務源, 每個 macroTask 處理完之後就從隊列中取出下一個時間最早進入的 macroTask 再重執行

任務源:

1. script
2. 事件
3. Dom交互
4. I/O
5. UI Render
6. setTimeout
7. setInterval
8. requestAnimationFrame
.....

也就是說碰到以上幾種情況就會產生一個 macroTask 並且推入到隊列中

miscroTask(微任務)

執行完每個 macroTask 之後,主線程會去檢查該macroTask下的 microTask 是否爲空,如果不爲空,則按照時間順序從早到晚取出,如果途中有遇到新的 microTask, 那麼會繼續將該 microTask 推入到 microTask 隊列裏

UI Render(重點)

伴隨着 miscroTask 隊列的清空,主線程就會執行 UI Render, 也就是渲染界面,但是瀏覽器它並不會每次在 UI Render 任務下一定會渲染界面,視情況而言,現在主流瀏覽器一般都是按照 60HZ 也就是 16.7ms 刷新頻率進行渲染 (不是精確估量),一個 macroTask 通常是小於 16.7ms, 所以瀏覽器每次會根據情況進行渲染

總結下一個循環

1. 從macroTask隊列裏取出最早添加進去的
2. 開始執行task, 途中如果遇到新的macroTask,就會將其添加到macroTask隊列的最後面
3. 執行完macroTask之後,event loop會去尋找microTask隊列
4. 同樣的道理,如果途中遇到新的microTask,將其放入該macroTask下的microTask隊列最後面
5. 執行完microTask,會執行UI Render macroTask
6. 瀏覽器會根據現有情況決定是否更新DOM,通常是按照60HZ的頻率去更新
7. 至此,一個event loop結束了

nextTick

我們開始分析 nextTick 根據上圖

  1. 我們看到 nextTick 的幾種寫法:
    1. this.$nextTick(cb)
    2. this.$nextTick().then(cb)
  1. 所有的 cb 都會被放入到 callbacks 數組裏,等待一次性調用

上圖中我們看到了主要是由 timerFunc 這個函數來進行調用回調, 那麼我們下面來着重介紹這個函數,首先看下源碼

我們可以看到 timerFunc 在不同情況下不同的賦值情況

  1. 首先會判斷瀏覽器是否支持 promise 屬性, 如果支持, timerFunc 就會被賦值成 Promise, 這裏有個小小的問題,那就是在 ios 下,雖然是具備 Pormise 對象,並且會將它推入到 microTask 隊列裏,但是隊列卻不會更新,這個時候需要添加一個 macroTask 來強制刷新 microTask 隊列

  2. MutationObserver, 相信很多人並不清楚這個 Api, 這是一個能夠監聽 DOM 變化的 API,並且屬於 microTask, 優先級低於 Promise 在創建一個新的文本節點後,手動更改其文本節點來觸發 microTask,

    這裏會有個小小的問題:

    該文本節點渲染成功後, 一定能代表其他的 DOM 渲染成功了嗎?

    這是個備選方案, 主要還是因爲它是一個微任務,所以才使用它,並不是因爲它監聽了 DOM

  3. 微任務都失敗後, 退而求其次,選擇 setImmediate, 這是一個只有高版本 IE 和 Edge 瀏覽器纔可能擁有的 API, 其主要是用於計算大量數據的時候使用

  4. 最後就是 setTimeout

看到這裏,你會不會有疑惑?

上面的代碼並沒有說明 nextTick 是在監聽 DOM 更新後才執行的?What???? 當時腦袋就 duang 了一下

那麼接下來說的就是重中之重

DOM Tree 的更新是實時的,DOM Tree 的更新是實時的,DOM Tree 的更新是實時的, 重要的事說 3 遍, 這意味着你無需去監聽 DOM 更新, 你對 DOM 的操作是能夠實時得到反饋的,上一行代碼操作了 DOM,下一行就能獲取到

那麼有人就會產生疑惑了, nextTick 究竟是幹嘛的?

nextTick 的作用是將收集 Watcher 從隊列中一個個取出,並且更改數據,來一次性渲染 DOM, 我們知道操作 DOM 的代價是昂貴的, 瀏覽器打開一個網頁後會開啓一個進程,進程是由線程組成的, 那麼在打開的同時,

1. GUI渲染線程
2. js引擎線程(主線程)
3. EventLoop輪訓處理線程
4. 其他線程,例如網絡

跨線程操作代價是昂貴的,所以做到一次性渲染 Dom, 可以有效的優化性能!!

總結

nextTick 並不是用來監聽 DOM 變更,因爲 DOM 變更是能夠實時獲取到的,它的作用是一次性更改數據,並且渲染 DOM。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/tknXxlpvqI2qaluS-wmoIA