Home > Article > Web Front-end > What function does nextTick have in Vue? Simple implementation of nextTick
This article brings you what is the function of nextTick in Vue? How to achieve? , has certain reference value, friends in need can refer to it, I hope it will be helpful to you.
nextTick is a core function of Vue, and nextTick is often used in Vue’s internal implementation. However, many novices do not understand the principle of nextTick, or even the function of nextTick.
So, let’s first take a look at what nextTick is.
Look at the description of the official documentation:
Execute a delayed callback after the next DOM update cycle ends. Use this method immediately after modifying data to get the updated DOM.
Look at the official example again:
// 修改数据 vm.msg = 'Hello' // DOM 还没有更新 Vue.nextTick(function () { // DOM 更新了 }) // 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示) Vue.nextTick() .then(function () { // DOM 更新了 })
New from 2.1.0: If no callback is provided and in an environment that supports Promise, a Promise is returned. Please note that Vue does not come with Promise polyfills, so if your target browser does not natively support Promise (IE: why are you looking at me), you have to provide the polyfill yourself.
As you can see, the main function of nextTick is to change the data and let the callback function act on the dom update. Many people are confused when they see this. Why do I need to execute the callback function after the DOM is updated? After I modify the data, doesn't the DOM automatically update?
This is related to Event Loop in JS. There are countless online tutorials, so I won’t go into details here. It is recommended that you understand the Event Loop before continuing to read this article.
A practical example:
We have a table with a pager, and the first item needs to be selected every time the page is turned. Under normal circumstances, what we want is to click the page turner, obtain data from the background, update the table data, and manipulate the table API to select the first item.
However, you will find that the table data is updated, but the first item is not selected. Because when you select the first item, although the data is updated, the DOM is not updated. At this point, you can use nextTick to manipulate the selection of the first item in the table after the DOM is updated.
So, what exactly does nextTick do to execute the callback function after the DOM is updated?
The source code of nextTick is located in src/core/util/next-tick.js, with a total of 118 lines. It is very short and concise, and is very suitable for students who are reading the source code for the first time.
nextTick source code is mainly divided into two parts:
1. Capability detection
2. Execute the callback queue in different ways according to the capability detection
This part is actually very simple. As we all know, Event Loop is divided into macro tasks and micro tasks. Regardless of whether a macro task or a micro task is executed, it will enter the next tick after completion, and will Perform UI rendering between ticks.
However, macro tasks take more time than micro tasks, so if the browser supports it, use micro tasks first. If the browser does not support microtasks, use macrotasks; however, the efficiency of various macrotasks is also different, and you need to use different macrotasks based on the browser's support.
nextTick follows this idea when it comes to ability testing.
// Determine (macro) task defer implementation. // Technically setImmediate should be the ideal choice, but it's only available // in IE. The only polyfill that consistently queues the callback after all DOM // events triggered in the same loop is by using MessageChannel. /* istanbul ignore if */ // 如果浏览器不支持Promise,使用宏任务来执行nextTick回调函数队列 // 能力检测,测试浏览器是否支持原生的setImmediate(setImmediate只在IE中有效) if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // 如果支持,宏任务( macro task)使用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 { /* istanbul ignore next */ // 都不支持的情况下,使用setTimeout macroTimerFunc = () => { setTimeout(flushCallbacks, 0) } }
First, check whether the browser supports setImmediate. If not, use MessageChannel. If not, you can only use setTimeout, which is the least efficient but the most compatible.
After that, check whether the browser supports Promise. If it does, use Promise to execute the callback function queue. After all, microtasks are faster than macrotasks. If it is not supported, you can only use macro tasks to execute the callback function queue.
The code for executing the callback function queue is just at the beginning and the end
// 回调函数队列 const callbacks = [] // 异步锁 let pending = false // 执行回调函数 function flushCallbacks () { // 重置异步锁 pending = false // 防止出现nextTick中包含nextTick时出现问题,在执行回调函数队列前,提前复制备份,清空回调函数队列 const copies = callbacks.slice(0) callbacks.length = 0 // 执行回调函数队列 for (let i = 0; i < copies.length; i++) { copies[i]() } } ... // 我们调用的nextTick函数 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() } } // $flow-disable-line // 2.1.0新增,如果没有提供回调,并且支持Promise,返回一个Promise if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
The overall process is to receive the callback function and push the callback function into the callback function queue middle.
At the same time, when receiving the first callback function, execute the corresponding asynchronous method in the capability test (the callback function queue is called in the asynchronous method).
How to ensure that the asynchronous method is only executed when the first callback function is received?
nextTick source code uses the concept of an asynchronous lock, that is, when receiving the first callback function, first close the lock and execute the asynchronous method. At this time, the browser is waiting for the synchronous code to be executed before executing the asynchronous code.
An analogy: It is equivalent to a group of passengers preparing to get on the bus. When the first passenger gets on the bus, the bus starts to start and is ready to go. After all passengers get on the bus, you can officially start driving.
Of course, there is a difficult point to understand when executing the flushCallbacks function, namely: why do you need to back up the callback function queue? Is the callback function queue also executed?
Because, there will be such a situation: nextTick applies nextTick. If flushCallbacks does not perform special processing and directly executes the callback function in a loop, the callback function in nextTick will enter the callback queue. This is equivalent to passengers on the next bus boarding the previous bus.
Having said so much, let’s implement a simple nextTick:
let callbacks = [] let pending = false function nextTick (cb) { callbacks.push(cb) if (!pending) { pending = true setTimeout(flushCallback, 0) } } function flushCallback () { pending = false let copies = callbacks.slice() callbacks.length = 0 copies.forEach(copy => { copy() }) }
As you can see, in the simple version of nextTick, through nextTick Receive the callback function and execute the callback function asynchronously through setTimeout. In this way, the callback function can be executed in the next tick, that is, the callback function is executed after the UI is re-rendered.
The above is the detailed content of What function does nextTick have in Vue? Simple implementation of nextTick. For more information, please follow other related articles on the PHP Chinese website!