Editor’s note: this article is reprinted by big leopard miscellaneous public number Lu Big Leopard authorized strange dance weekly. Let’s learn!

Vue has a special API called nextTick. According to the official documentation, it can execute a callback after the DOM is updated, as follows:

// Modify the data

vm.msg = 'Hello'

// DOM has not been updated yet

Vue.nextTick(function () {

// DOM is updated

})

Copy the code

Although the MVVM framework does not recommend DOM access, it does sometimes require DOM manipulation, especially when working with third-party plug-ins. NextTick provides a bridge to make sure we’re working with the updated DOM.

This article starts with the question: How does vue detect when DOM updates are complete?

If you look at your front-end repository, the only API that can listen for DOM changes seems to be the MutationObserver, or MO.

Understand MutationObserver

MutationObserver is a new attribute of HTML5, which is used to listen to DOM modification events. It can listen to the changes of node attributes, text content, child nodes, etc. It is a powerful tool. The basic usage is as follows:

// the basic usage of MO

var observer = new MutationObserver(function(){

// This is the callback function

Console. log('DOM has been modified! ');

});

var article = document.querySelector('article');

observer.observer(article);

Copy the code

The use of MO is not the focus of this article. Does vue use MO to listen for DOM updates?

Take a look at the vue source code and, where nextTick is implemented, you can actually see code like this:

/ / [email protected] / SRC/core/util/env. Js

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)

  }

}

Copy the code

Briefly, if you detect that the browser supports MO, you create a text node and listen for changes to that text node to trigger the execution of the nextTickHandler (that is, the DOM update completion callback). In the following code, manually modify the text node properties so that you can enter the callback function.

General sweep a glance, seems to be able to get a solid hammer: oh! Vue uses the MutationObserver to listen for DOM updates!

Doesn’t it feel like something’s wrong? Let’s think about it for a moment:

  1. Vue creates a text node to listen for DOM updates in the template.

  2. Does updating your own text node mean updating other DOM nodes? What reason is this!

It seems that the above conclusion is not correct, this time we need to talk about js event loop mechanism.

Event Loop

In the js operating environment, we are only talking about the browser, usually accompanied by a lot of events, such as user clicks, page rendering, script execution, network requests, and so on. To coordinate the processing of these events, browsers use an event loop mechanism.

In short, event loops maintain one or more Task queues to which the events mentioned above act as task sources to join tasks. There is a continuous thread that processes these tasks, and each time it completes, it is removed from the queue. This is an event loop, as shown in the figure below:

We usually use setTimeout to execute asynchronous code, which is to add a task to the end of the task queue and wait for it to execute after all the previous tasks have completed.

The key is that at the end of each Event loop, there is a UI Render step that updates the DOM. Why is the standard designed this way? Consider the following code:

for(let i=0; i<100; i++){

    dom.style.left = i + 'px';

}

Copy the code

Does the browser do 100 DOM updates? Obviously not. It would take too much performance. In fact, the 100 for loops belong to the same task, and the browser only updates the DOM once the task has finished executing.

Here’s the idea: just put the code in nextTick after the UI Render step, and you’ll be able to access the updated DOM.

This is the idea behind VUE. Instead of using MO to listen for DOM changes, VUE uses queue control. So how does VUE do queue control? It’s natural to think of setTimeout and put the code that nextTick is going to execute at the end of the queue as the next task.

However, things are not that simple. Vue’s data response process consists of: data changes -> notify Watcher-> update DOM. Changes to the data are out of our control and can happen at any time. If it happens to happen before repaint, multiple renders will occur. This means wasted performance, which VUE does not want to see.

As a result, vUE’s queue control was well thought out (and changed a lot). Before we get there, we need to understand another important concept of Event Loop, MicroTask.

microtask

From the name, we could call it a microtask. Similarly, tasks in a task queue are also called macroTasks. The names are similar, but the properties are different.

Each event cycle contains a queue of Microtasks, which are executed and removed at the end of the cycle before the next event cycle begins.

Microtasks added to the MicroTask queue during the execution of a Microtask are also executed before the next event loop. That is, macroTask always waits until microTasks are all executed, and microTasks have a higher priority.

This feature of MicroTask is the perfect choice for queue control. Vue performs DOM updates and internally calls nextTick for asynchronous queue control. When we call nextTick ourselves, it appends our own callback function to the microtask that updated the DOM, ensuring that our code executes after the DOM update and avoiding multiple executions of setTimeout.

Common microtasks include Promise, MutationObserver, Object.observe(discard), and process.nexttick in NodeJS.

Yi? Vue uses MO to take advantage of its microtask features, rather than DOM listening. Yeah, that’s it. The core is microtask, with or without MO. In fact, mo-related code has been removed from Vue in version 2.5, as it is a new feature in HTML5 and still buggy on iOS.

The optimal microTask policy is Promise, and awkwardly, promises are a new addition to ES6 and have compatibility issues, so Vue faces a degraded strategy.

Vue’s demotion strategy

As we mentioned above, the best choice for queue control is MicroTask, and the best choice for MicroTask is Promise. But if the current environment doesn’t support Promises, vUE will have to be relegated to MacroTask for queue control.

What are macroTask’s alternatives? SetTimeout is mentioned earlier as one option, but it is not ideal. Because the minimum interval between setTimeout execution is about 4ms, there is a slight delay. Are there any other plans?

Not to keep in mind, in the Source code for Vue2.5, MacroTask degrades through setImmediate, MessageChannel, and setTimeout.

SetImmediate is the ideal solution, but only IE and NodeJS support it.

MessageChannel’s onMessage callback is also microTask, but it’s also a new API that faces compatibility awkwardness…

So the last resort is setTimeout, although it has execution delays and may cause multiple renders, which is a hopeless solution.

conclusion

The above is the realization principle of VUE’s nextTick method, which can be summarized as follows:

  1. Vue uses an asynchronous queue to control when DOM updates and nextTick callbacks are executed

  2. Due to its high-priority nature, MicroTask ensures that the microtasks in the queue are completed before an event loop

  3. Due to compatibility issues, Vue had to downgrade microtask to MacroTask

Related information:

The event loop standard

https://html.spec.whatwg.org/multipage/webappapis.html#event-loops

NextTick change records for Vue2.5

https://github.com/vuejs/vue/commit/6e41679a96582da3e0a60bdbf123c33ba0e86b31

Source code analysis article

https://github.com/answershuto/learnVue/blob/master/docs/Vue.js%E5%BC%82%E6%AD%A5%E6%9B%B4%E6%96%B0DOM%E7%AD%96%E7%95%A5 %E5%8F%8AnextTick.MarkDown

About Weird Dance Weekly

Qiwu Weekly is a front-end technology community operated by qiwu Group, a professional front-end team of 360 Company. After paying attention to the public number, directly send the link to the background can contribute to us.