您好,登錄后才能下訂單哦!
這篇文章主要介紹“Vue2異步更新及nextTick原理是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Vue2異步更新及nextTick原理是什么”文章能幫助大家解決問題。
瀏覽器是多線程的,例如GUI渲染線程、JS引擎線程、事件監聽線程等。
javascript 執行機制就是借用瀏覽器的多線程機制,再基于 Event Loop 事件循環機制實現的。其實現了單線程異步效果
Event Loop 步驟大致如下:
瀏覽器加載頁面時,除了開辟堆棧內存外,還會創建兩個隊列 Web API:任務監聽隊列,監測異步任務是否可以執行 Task Queue:任務隊列,分為異步宏任務隊列和異步微任務隊列 當主線程自上而下執行代碼過程中,如果遇到異步代碼,則把異步任務放到 Web API 中去監聽 瀏覽器會開辟新的線程去監聽是否可以執行 不會阻礙主線程的渲染,它會繼續向下執行同步代碼 當異步任務被監測為可以執行了(有了運行結果),也不會立即去執行,而是在 task queue 中放置一個事件,排隊等待執行 根據微任務還是宏任務,放在不同的隊列中 誰先進來排隊的,誰在各自隊伍的最前面 執行棧中的所有同步任務執行完畢,主線程空閑下來,此時會去 task queue 中把正在排隊的事件,按照順序取出來,進入主線程執行 微任務優先級比較高。當執行棧為空時,先去執行微任務隊列中的事件,直到微任務隊列為空,才會去執行宏任務隊列中的事件 上述過程會不斷重復,也就是常說的事件循環(Event Loop)
task 又分為宏任務(macro task)和微任務(micro task)兩大類,在瀏覽器環境中
常見的 macro task 有 script(整體代碼)、setTimeout/setInterval/setImmediate
、XMLHttpRequest/fetch
,DOM事件(如鼠標點擊、滾動頁面、放大縮小等),渲染事件(解析 DOM、計算布局、繪制) 常見的 micro task 有 Promise.then/catch/finally
、async/await
、MutationObserver
需要注意的是!!!如果處理微任務的過程中有新的微任務添加進來了,添加的速度一直比執行快,則永遠執行微任務
下面的代碼永遠不會打印宏任務輸出
function macroFn(){ setTimeout(() => { console.log('>>>>MA') },0) } function microFn(){ Promise.resolve().then(() => { console.log('mi') microFn() }) } macroFn() microFn()
vue2.7 源碼中,有一個單獨的文件src/core/util/next-tick.js
去維護 nextTick,有興趣的同學可以自行去觀看
vue2.7 源碼中,nextTick并沒有直接使用某個 API ,而是采用了優雅降級的方案去實現異步更新
在內部會嘗試使用原生的Promise.then (IE不支持)
、MutationObserver
和 setImmediate (高版本IE專享)
,如果執行環境還不支持的話,則會采用 setTimeout(fn, 0)
需要注意的是,我們維護了一個 callbacks,用于存儲 nextTick 回調
這樣就保證了在同一個 tick 內多次調用 nextTick,只需創建一個異步任務,就可以依次執行 callbacks 中的所有 nextTick 回調。而不是去開啟多個異步任務去處理。
let callbacks = [] // 存儲 nextTick 回調 let waiting = false // 防抖 // 按照順序依次執行 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); // 這里傳入的回調是異步執行的 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 內部的異步更新渲染也使用了 nextTick
在 Watcher 類的 update 更新方法中,我們調用了 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,內部 watcher 異步更新 * @decs 把當前的 watcher 暫存起來,在一個tick周期內,不管我們的 update 執行多少次,只會執行一輪刷新操作 */ 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周期內,不管我們的 update 執行多少次,只會執行一輪刷新操作 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 內部既有同步代碼又有異步代碼。
例如 維護 callbacks 隊列是同步任務;執行隊列中的方法是異步任務
2. nextTick 回調的執行是微任務還是宏任務?
針對 vue2.7 來說,nextTick并沒有直接使用某個 API ,而是采用了優雅降級的方案去實現異步更新。
在內部會嘗試使用原生的Promise.then (微任務)
、MutationObserver (微任務)
和 setImmediate (宏任務)
,如果執行環境還不支持的話,則會采用 setTimeout (宏任務)
可以理解為 99% 的場景下都是微任務,只有在不支持 Promise 和 MutationObserver API的瀏覽器中,才會是宏任務,例如 IE9 、IE10
3. 為什么要封裝 nextTick?而不是使用某個具體的 API?
優雅降級。盡量使用微任務,盡可能縮短渲染周期
保證統一性。nextTick 可以暴露給用戶,保證用戶在修改數據之后立即使用這個方法,可以獲取更新后的 DOM
this.name = 'libc' this.$nextTick(()=>{ console.log(document.querySelector('.user').innerHTML) });
關于“Vue2異步更新及nextTick原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。