Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to see the principle and execution Mechanism of Vue.nextTick from the Javascript event Loop

2025-01-21 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

Today, I will talk to you about how to see the principle and execution mechanism of Vue.nextTick from Javascript event loop. Many people may not know much about it. In order to let everyone know more, Xiaobian summarizes the following contents for everyone. I hope everyone can gain something according to this article.

catch a whale

One of Vue's characteristics is that it is responsive, but sometimes the data is updated and we see that the DOM on the page does not update immediately. If we need to execute a piece of code after a DOM update, we can do it with nextTick.

Let's start with an example.

export default { data() { return { msg: 0 } }, mounted() { this.msg = 1 this.msg = 2 this.msg = 3 }, watch: { msg() { console.log(this.msg) } } }

The result here is to output only a 3 instead of 1, 2, 3 in sequence. Why is that?

The official vue documentation explains this:

Vue performs DOM updates asynchronously. Vue will open a queue whenever a data change is observed and buffer all data changes that occur in the same event cycle. If the same watcher is triggered multiple times, it will be pushed into the queue only once. This de-duplication of data in buffering is important to avoid unnecessary computation and DOM manipulation. Then, in the next event loop "tick," Vue refreshes the queue and performs the actual (deduplicated) work. Vue internally tries to use the native Promise.then and MessageChannel for asynchronous queues, using setTimeout(fn, 0) instead if the execution environment does not support it.

If there is a case where the value of a variable a under the mounted hook function is executed 1000 times by the ++ loop. Each time ++ occurs, setter->Dep->Watcher->update->run is triggered responsively. If you don't update the view asynchronously at this time, then each time ++ directly manipulates the DOM once, which is very performance-intensive. So Vue implements a queue, and at the next Tick(or the microtask phase of the current Tick), the Watcher run in the queue will be uniformly executed. Watchers with the same id are not added to the queue repeatedly, so Watcher runs are not performed 1000 times. The end result is to change the value of a directly from 1 to 1000, which greatly improves performance.

In vue, data monitoring is implemented by overriding the set and get methods in Object.defineProperty. Vue updates DOM asynchronously. Whenever data changes are observed, vue starts a queue to cache all data changes in the same event loop. When the next eventLoop occurs, the queue will be emptied and DOM updates will be performed.

To understand the execution mechanism of vue.nextTick, let's first look at javascript's event loop.

JS Event Loop

Js task queue is divided into synchronous tasks and asynchronous tasks, all synchronous tasks are executed in the main thread. Asynchronous tasks may be in a macrotask or microtask, and asynchronous tasks enter the Event Table and register functions. When the specified event is complete, the Event Table moves this function into the Event Queue. If the task execution in the main thread is completed and empty, it will go to the Event Queue to read the corresponding function and enter the main thread execution. This process repeats itself, often referred to as an Event Loop.

1. macro-task:

Each execution of code on the stack is a macro task (including fetching an event callback from the event queue and placing it on the stack for execution). In order for the browser to enable the internal (macro)task and DOM task of js to be executed in an orderly manner, the page will be re-rendered after the execution of one (macro)task and before the execution of the next (macro)task. Macro tasks mainly include:

script(whole code)

setTimeout / setInterval

setImmediate(Node.js environment)

I/O

UI render

postMessage

MessageChannel

2. micro-task:

It can be understood that it is a task that is executed immediately after the execution of the current task. That is, after the current task, before the next task, before rendering. So its response time is faster than setTimeout(setTimeout is task) because there is no need to wait for rendering. That is, after a macrotask is executed, all microtasks generated during its execution are executed (before rendering). Microtask mainly includes:

process.nextTick(Node.js environment)

Promise

Async/Await

MutationObserver(html5 new feature)

3. summary

Execute the main thread first

Encounter macro queue (macrotask) Put into macro queue (macrotask)

Encounter microqueue (microtask) Put into microqueue (microtask)

Main thread execution complete

microtask execution, microtask execution completed

Execute a task in a macrotask once, and the execution is complete

Execution of microtask, execution complete

Cycle in turn...

Vue.nextTick source code

vue is a two-way data binding method to drive data updates, although this can avoid direct manipulation of DOM, improve performance, but sometimes we can not avoid the need to manipulate DOM, then the Vue.nextTick(callback) comes out, it accepts a callback function, after the DOM update is complete, this callback function will be called. vue.nextTick or vue. prototype.\$ nextTick is directly used by the nextTick closure function.

export 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]() } } ... })() 使用数组callbacks保存回调函数,pending表示当前状态,使用函数nextTickHandler 来执行回调队列。在该方法内,先通过slice(0)保存了回调队列的一个副本,通过设置 callbacks.length = 0清空回调队列,最后使用循环执行在副本里的所有函数。 if (typeof Promise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve() var logError = err =>

{ console.error(err) } timerFunc = () => { p.then(nextTickHandler).catch(logError) if (isIOS) setTimeout(noop) } } else if (typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]')) { var counter = 1 var observer = new MutationObserver(nextTickHandler) var textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } } else { timeFunc = () => {

队列控制的最佳选择是microtask,而microtask的最佳选择是Promise。但如果当前环境不支持 Promise,就检测到浏览器是否支持 MO,是则创建一个文本节点,监听这个文本节点的改动事件,以此来触发nextTickHandler(也就是 DOM 更新完毕回调)的执行。此外因为兼容性问题,vue 不得不做了microtask向macrotask 的降级方案。

为让这个回调函数延迟执行,vue 优先用promise来实现,其次是 html5 的 MutationObserver,然后是setTimeout。前两者属于microtask,后一个属于 macrotask。下面来看最后一部分。

return function queueNextTick(cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) cb.call(ctx) if (_resolve) _resolve(ctx) }) if (!pending) { pending = true timerFunc() } if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }

这就是我们真正调用的nextTick函数,在一个event loop内它会将调用 nextTick的cb 回调函数都放入 callbacks 中,pending 用于判断是否有队列正在执行回调,例如有可能在 nextTick 中还有一个 nextTick,此时就应该属于下一个循环了。最后几行代码是 promise 化,可以将 nextTick 按照 promise 方式去书写(暂且用的较少)。

应用场景

场景一、点击按钮显示原本以 v-show = false 隐藏起来的输入框,并获取焦点。

showInput(){ this.showit = true document.getElementById("keywords").focus() }

以上的写法在第一个 tick 里,因为获取不到输入框,自然也获取不到焦点。如果我们改成以下的写法,在 DOM 更新后就可以获取到输入框焦点了。

showsou(){ this.showit = true this.$nextTick(function () { // DOM 更新了 document.getElementById("keywords").focus() }) }

场景二、获取元素属,点击获取元素宽度。

{{ message }}

获取p元素宽度 getMyWidth() { this.showMe = true; thisthis.message = this.$refs.myWidth.offsetWidth; //报错 TypeError: this.$refs.myWidth is undefined this.$nextTick(()=>{ //dom元素更新后执行,此时能拿到p元素的属性 thisthis.message = this.$refs.myWidth.offsetWidth; }) }看完上述内容,你们对如何从Javascript事件循环看出Vue.nextTick的原理和执行机制有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report