Home >Web Front-end >JS Tutorial >A brief analysis of the implementation method of Vue.nextTick
This article mainly introduces the implementation method of Vue.nextTick. This is a source code analysis of the vue.nextTick API implementation after event loop and MicroTask. The editor thinks it’s pretty good, so I’d like to share it with you now and give it as a reference. Let’s follow the editor to take a look, I hope it can help everyone.
Preheat, write a sleep function
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)
Explain the sleep function
async function Function execution is suspended when await PromiseFn() is performed, and we also know that this PromiseFn is currently executed within a microTask. When the microTask has not been executed, the subsequent macroTask will not be executed. We also implemented a sleep function through the event loop feature of microTask to prevent the execution of console.log
Process
1Execute console.log('start')
2Execute await to pause the execution, wait for the PromiseFn after the await function to complete execution in microTask
3In the sleep function, delay ms to return
4Execute console.log('end') after returning to resolve
nextTick API
How to use nextTick in vue
vue.nextTick(() => { // todo... })
After understanding the usage, take a look at the source code
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 !== 'undefined' && isNative(Promise)) { var p = Promise.resolve() var logError = err => { console.error(err) } timerFunc = () => { p.then(nextTickHandler).catch(logError) // 重点 } } else if ('!isIE MutationObserver') { 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 !== 'undefined') { return new Promise((resolve, reject) => { _resolve =resolve }) } } })() // 自执行函数
If you take a rough look at the source code, you can understand that nextTick api is a self-executing function
Since it is a self-executing function, look directly at its return type, 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 !== 'undefined') { return new Promise((resolve, reject) => { _resolve =resolve }) } }
Only focus on the main process queueNextTick function and push the () we passed in => { // todo... } into the callbacks
if (typeof Promise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve() var logError = err => { console.error(err) } timerFunc = () => { p.then(nextTickHandler).catch(logError) // 重点 } } else if ('!isIE MutationObserver') { 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) // 重点 } }
We can look at this section The three marked points indicate that Promise, MutationObserver or setTimeout(fn, 0) are used in different browser environments to execute nextTickHandler
function nextTickHandler () { pending = false const copies = callbacks.slice(0) // 复制 callbacks.length = 0 // 清空 for (let i = 0; i < copies.length; i++) { copies[i]() // 逐个执行 } }
nextTickHandler is to put what we put before callbacks() => { // todo... } is executed within the current tasks.
Write a simple nextTick
The source code may be complicated, let’s write a simple nextTick ourselves
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, 'nextTick') })
We can see from here that the principle of nextTick is to return a Promise, and our todo code is executed in this Promise. Now we can continue to simplify
const simpleNextTick = (function () { return function queueNextTick (cb) { timerFunc = () => { return Promise.resolve().then(() => { cb() }) } timerFunc() } })() simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') })
directly Write it like this.
const simpleNextTick = function queueNextTick (cb) { timerFunc = () => { return Promise.resolve().then(() => { cb() }) } timerFunc() } simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') })
This time we also simplify the self-executing function
##
const simpleNextTick = function queueNextTick (cb) { return Promise.resolve().then(cb) } simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') })Now we simplify it directly to the end, now I found that the core content of nextTick is Promise, a microtask. Now we return to vue’s nextTick API official example
<p id="example">{{message}}</p> var vm = new Vue({ el: '#example', data: { message: '123' } }) vm.message = 'new message' // 更改数据 vm.$el.textContent === 'new message' // false Vue.nextTick(function () { vm.$el.textContent === 'new message' // true })It turns out that the dom update after the data in vue is updated is to be executed after the next event loop of.
The principle of using nextTick is mainly to solve the scenario of operating DOM immediately after updating data in a single event.
const simpleNextTick = function queueNextTick (cb) { return Promise.resolve().then(cb) } simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') // 也可以换成ajax请求 })
function sleep (ms) { return new Promise(resolve => setTimeout(resolve, ms) // 也可以换成ajax请求 } async function oneTick (ms) { console.log('start') await sleep(ms) console.log('end') } oneTick(3000)We see that the execution results of nextTick and oneTick we wrote are so similar. The only difference is that nextTick wraps the callback with a Promise and returns it and executes it, while oneTick uses await to execute a Promise function, and this Promise has its own wrapped webapi function. When making an ajax request, can we directly use axios to return the Promise library?
async function getData () { const data = await axios.get(url) // 操作data的数据来改变dom return data }This can also achieve the same effect as nextTick Finally, we can also see from the source code that when the browser environment does not support Promise, you can use MutationObserver or setTimeout(cb, 0) to achieve the same effect. But the final core is microTaskRelated recommendations:
process.nextTick usage example in Node.js
node Analysis of the difference between timer nextTick() and setImmediate() in .js_node.js
Usage example of process.nextTick in Node.js_node.js
The above is the detailed content of A brief analysis of the implementation method of Vue.nextTick. For more information, please follow other related articles on the PHP Chinese website!