溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue2異步更新及nextTick原理是什么

發(fā)布時間:2023-05-08 16:52:52 來源:億速云 閱讀:146 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Vue2異步更新及nextTick原理是什么”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Vue2異步更新及nextTick原理是什么”文章能幫助大家解決問題。

JavaScript 執(zhí)行機制

瀏覽器是多線程的,例如GUI渲染線程、JS引擎線程、事件監(jiān)聽線程等。

javascript 執(zhí)行機制就是借用瀏覽器的多線程機制,再基于 Event Loop 事件循環(huán)機制實現(xiàn)的。其實現(xiàn)了單線程異步效果

Event Loop 步驟大致如下:

瀏覽器加載頁面時,除了開辟堆棧內(nèi)存外,還會創(chuàng)建兩個隊列 Web API:任務監(jiān)聽隊列,監(jiān)測異步任務是否可以執(zhí)行 Task Queue:任務隊列,分為異步宏任務隊列和異步微任務隊列 當主線程自上而下執(zhí)行代碼過程中,如果遇到異步代碼,則把異步任務放到 Web API 中去監(jiān)聽 瀏覽器會開辟新的線程去監(jiān)聽是否可以執(zhí)行 不會阻礙主線程的渲染,它會繼續(xù)向下執(zhí)行同步代碼 當異步任務被監(jiān)測為可以執(zhí)行了(有了運行結(jié)果),也不會立即去執(zhí)行,而是在 task queue 中放置一個事件,排隊等待執(zhí)行 根據(jù)微任務還是宏任務,放在不同的隊列中 誰先進來排隊的,誰在各自隊伍的最前面 執(zhí)行棧中的所有同步任務執(zhí)行完畢,主線程空閑下來,此時會去 task queue 中把正在排隊的事件,按照順序取出來,進入主線程執(zhí)行 微任務優(yōu)先級比較高。當執(zhí)行棧為空時,先去執(zhí)行微任務隊列中的事件,直到微任務隊列為空,才會去執(zhí)行宏任務隊列中的事件 上述過程會不斷重復,也就是常說的事件循環(huán)(Event Loop)

task 又分為宏任務(macro task)和微任務(micro task)兩大類,在瀏覽器環(huán)境中

常見的 macro task 有 script(整體代碼)、setTimeout/setInterval/setImmediate、XMLHttpRequest/fetch,DOM事件(如鼠標點擊、滾動頁面、放大縮小等),渲染事件(解析 DOM、計算布局、繪制) 常見的 micro task 有 Promise.then/catch/finally、async/await、MutationObserver

需要注意的是!?。∪绻幚砦⑷蝿盏倪^程中有新的微任務添加進來了,添加的速度一直比執(zhí)行快,則永遠執(zhí)行微任務

下面的代碼永遠不會打印宏任務輸出

function macroFn(){
   setTimeout(() => {
     console.log('>>>>MA')
   },0)
}
function microFn(){
    Promise.resolve().then(() => {
        console.log('mi')
        microFn()
    })
}
macroFn()
microFn()

nextTick實現(xiàn)原理

vue2.7 源碼中,有一個單獨的文件src/core/util/next-tick.js去維護 nextTick,有興趣的同學可以自行去觀看

vue2.7 源碼中,nextTick并沒有直接使用某個 API ,而是采用了優(yōu)雅降級的方案去實現(xiàn)異步更新

在內(nèi)部會嘗試使用原生的Promise.then (IE不支持)、MutationObserversetImmediate (高版本IE專享),如果執(zhí)行環(huán)境還不支持的話,則會采用 setTimeout(fn, 0)

需要注意的是,我們維護了一個 callbacks,用于存儲 nextTick 回調(diào)

這樣就保證了在同一個 tick 內(nèi)多次調(diào)用 nextTick,只需創(chuàng)建一個異步任務,就可以依次執(zhí)行 callbacks 中的所有 nextTick 回調(diào)。而不是去開啟多個異步任務去處理。

let callbacks = [] // 存儲 nextTick 回調(diào)
let waiting = false // 防抖

// 按照順序依次執(zhí)行 callbacks 中的方法
function flushCallbacks() {
  let cbs = callbacks.slice(0)
  waiting = false
  callbacks = []
  cbs.forEach(cb => cb()) 
}


let timerFunc;
if (Promise) {
    timerFunc = () => {
        Promise.resolve().then(flushCallbacks)
    }
}else if(MutationObserver){
    let observer = new MutationObserver(flushCallbacks); // 這里傳入的回調(diào)是異步執(zhí)行的
    let textNode = document.createTextNode(1);
    observer.observe(textNode,{
        characterData:true
    });
    timerFunc = () => {
        textNode.textContent = 2;
    }
}else if(setImmediate){
    timerFunc = () => {
       setImmediate(flushCallbacks);
    }
}else{
    timerFunc = () => {
        setTimeout(flushCallbacks);
     }
}

export function nextTick(cb) {
  callbacks.push(cb) // 維護 nextTick 中的 cakllback 方法
  
  if (!waiting) {
    timerFunc()
    waiting = true
  }
}

異步更新

vue 內(nèi)部的異步更新渲染也使用了 nextTick

在 Watcher 類的 update 更新方法中,我們調(diào)用了 queueWatcher 異步隊列更新方法,該方法在 vue2.7源碼中的 src/core/util/scheduler.js 文件中維護

import { queueWatcher } from './scheduler'

class Watcher {
	...
  // 重新渲染
  update() {
    console.log('watcher-update')
    queueWatcher(this) // watcher 異步更新
  }
}

src/core/util/scheduler.js

import { nextTick } from '../util/next-tick'

/**
 * @name QueueWatcher,內(nèi)部 watcher 異步更新
 * @decs 把當前的 watcher 暫存起來,在一個tick周期內(nèi),不管我們的 update 執(zhí)行多少次,只會執(zhí)行一輪刷新操作
 */

let queue = []
let has = {}
let pending = false // 防抖

function flushSchedulerQueue() {
  let flushQueue = queue.slice(0)
  queue = []
  has = {}
  pending = false
  flushQueue.forEach(q => q.run()) // 在刷新的過程中可能還有新的 watcher,重新放到 queue 中
}

// 在一個tick周期內(nèi),不管我們的 update 執(zhí)行多少次,只會執(zhí)行一輪刷新操作
export function queueWatcher(watcher) {
  const id = watcher.id
  if (!has[id]) {
    queue.push(watcher)
    has[id] = true
    if (!pending) {
      nextTick(flushSchedulerQueue)
      pending = true
    }
  }
}

常見問題

1. nexTick 是異步還是同步?

這個不能一概而論,nextTick 內(nèi)部既有同步代碼又有異步代碼。

例如 維護 callbacks 隊列是同步任務;執(zhí)行隊列中的方法是異步任務

2. nextTick 回調(diào)的執(zhí)行是微任務還是宏任務?

針對 vue2.7 來說,nextTick并沒有直接使用某個 API ,而是采用了優(yōu)雅降級的方案去實現(xiàn)異步更新。
在內(nèi)部會嘗試使用原生的Promise.then (微任務)、MutationObserver (微任務)setImmediate (宏任務),如果執(zhí)行環(huán)境還不支持的話,則會采用 setTimeout (宏任務)

可以理解為 99% 的場景下都是微任務,只有在不支持 Promise 和 MutationObserver API的瀏覽器中,才會是宏任務,例如 IE9 、IE10

3. 為什么要封裝 nextTick?而不是使用某個具體的 API?

優(yōu)雅降級。盡量使用微任務,盡可能縮短渲染周期

保證統(tǒng)一性。nextTick 可以暴露給用戶,保證用戶在修改數(shù)據(jù)之后立即使用這個方法,可以獲取更新后的 DOM

this.name = 'libc'

this.$nextTick(()=>{
  console.log(document.querySelector('.user').innerHTML)
});

關(guān)于“Vue2異步更新及nextTick原理是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI