您好,登錄后才能下訂單哦!
這篇“Vue2和Vue3的nextTick怎么實現”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Vue2和Vue3的nextTick怎么實現”文章吧。
在 Vue2 中,nextTick 的實現基于瀏覽器的異步任務隊列和微任務隊列。
在瀏覽器中,每個宏任務結束后會檢查微任務隊列,如果有任務則依次執行。當所有微任務執行完成后,才會執行下一個宏任務。因此可以通過將任務作為微任務添加到微任務隊列中,來確保任務在所有宏任務執行完畢后立即執行。
而使用 setTimeout 可以將任務添加到異步任務隊列中,在下一輪事件循環中執行。
在 Vue2 中,如果沒有指定執行環境,則會優先使用 Promise.then / MutationObserver,否則使用 setTimeout。
javascript // src/core/util/next-tick.js /* istanbul ignore next */ 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 microTimerFunc let macroTimerFunc let useMacroTask = false if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // 使用 setImmediate macroTimerFunc = () => { setImmediate(flushCallbacks) } } else if ( typeof MessageChannel !== 'undefined' && (isNative(MessageChannel) || // PhantomJS MessageChannel.toString() === '[object MessageChannelConstructor]') ) { const channel = new MessageChannel() const port = channel.port2 channel.port1.onmessage = flushCallbacks macroTimerFunc = () => { port.postMessage(1) } } else { // 使用 setTimeout macroTimerFunc = () => { setTimeout(flushCallbacks, 0) } } if (typeof Promise !== 'undefined' && isNative(Promise)) { // 使用 Promise.then const p = Promise.resolve() microTimerFunc = () => { p.then(flushCallbacks) } } else { // 使用 MutationObserver const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(1)) observer.observe(textNode, { characterData: true }) microTimerFunc = () => { textNode.data = String(1) } } export function nextTick(cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true if (useMacroTask) { macroTimerFunc() } else { microTimerFunc() } } if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
在 Vue2 中,可以通過設置 useMacroTask 來使 nextTick 方法使用宏任務或者微任務。
Vue2 中默認使用微任務,在沒有原生 Promise 和 MutationObserver 的情況下,才會改用 setTimeout。
javascript let microTimerFunc let macroTimerFunc let useMacroTask = false // 默認使用微任務 if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // 使用 setImmediate macroTimerFunc = () => { setImmediate(flushCallbacks) } } else if ( typeof MessageChannel !== 'undefined' && (isNative(MessageChannel) || // PhantomJS MessageChannel.toString() === '[object MessageChannelConstructor]') ) { const channel = new MessageChannel() const port = channel.port2 channel.port1.onmessage = flushCallbacks macroTimerFunc = () => { port.postMessage(1) } } else { // 使用 setTimeout macroTimerFunc = () => { setTimeout(flushCallbacks, 0) } } if (typeof Promise !== 'undefined' && isNative(Promise)) { // 使用 Promise.then const p = Promise.resolve() microTimerFunc = () => { p.then(flushCallbacks) } } else { // 使用 MutationObserver const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(1)) observer.observe(textNode, { characterData: true }) microTimerFunc = () => { textNode.data = String(1) } } export function nextTick(cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true if (useMacroTask) { macroTimerFunc() } else { microTimerFunc() } } if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
在 Vue2 中,nextTick 的實現原理基于瀏覽器的異步任務隊列和微任務隊列。Vue2 默認使用微任務,在沒有原生 Promise 和 MutationObserver 的情況下才會改用 setTimeout。
在 Vue3 中,nextTick 的實現有了較大變化,主要是為了解決瀏覽器對 Promise 的缺陷和問題。
在瀏覽器中,Promise 有一個缺陷:如果 Promise 在當前事件循環中被解決,那么在 then 回調函數之前添加的任務將不能在同一個任務中執行。
例如:
javascript Promise.resolve().then(() => { console.log('Promise 1') }).then(() => { console.log('Promise 2') }) console.log('Hello')
輸出結果為:
Hello Promise 1 Promise 2
這是因為 Promise 雖然是微任務,但是需要等到當前宏任務結束才能執行。
在 Vue3 中,通過使用 MutationObserver 和 Promise.resolve().then() 來解決 Promise 在瀏覽器中的缺陷。具體實現如下:
javascript const queue: Array<Function> = [] let has: { [key: number]: boolean } = {} let flushing = false let index = 0 function resetSchedulerState() { queue.length = 0 has = {} flushing = false } function flushSchedulerQueue() { flushing = true let job while ((job = queue.shift())) { if (!has[job.id]) { has[job.id] = true job() } } resetSchedulerState() } let macroTimerFunc let microTimerFunc if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { macroTimerFunc = () => { setImmediate(flushSchedulerQueue) } } else { macroTimerFunc = () => { setTimeout(flushSchedulerQueue, 0) } } if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve() microTimerFunc = () => { p.then(flushSchedulerQueue) if (isIOS) setTimeout(noop) } } else { microTimerFunc = macroTimerFunc } export function nextTick(fn?: Function): Promise<void> { const id = index++ const job = fn.bind(null) queue.push(job) if (!flushing) { if (useMacroTask) { macroTimerFunc() } else { microTimerFunc() } } if (!fn && typeof Promise !== 'undefined') { return new Promise(resolve => { resolvedPromise.then(() => { if (has[id] || !queue.includes(job)) { return } queue.splice(queue.indexOf(job), 1) resolve() }) }) } }
在 Vue3 中,nextTick 的實現原理基于MutationObserver 和 Promise.resolve().then(),通過 MutationObserver 監測 DOM 變化,在下一個微任務中執行回調函數。
而如果當前瀏覽器不支持原生 Promise,則使用 setTimeout 來模擬 Promise 的行為,并在回調函數執行前添加一個空的定時器來強制推遲執行(解決 iOS 中 setTimeout 在非激活標簽頁中的問題)。
如果需要等待所有回調函數執行完成,則可以通過返回一個 Promise 對象來實現。
javascript export function nextTick(fn?: Function): Promise<void> { const id = index++ const job = fn.bind(null) queue.push(job) if (!flushing) { if (useMacroTask) { macroTimerFunc() } else { microTimerFunc() } } if (!fn && typeof Promise !== 'undefined') { return new Promise(resolve => { resolvedPromise.then(() => { if (has[id] || !queue.includes(job)) { return } queue.splice(queue.indexOf(job), 1) resolve() }) }) } }
以上就是關于“Vue2和Vue3的nextTick怎么實現”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。