A simple understanding of nextTick in Vue and vuenexttick

Source: Internet
Author: User

A simple understanding of nextTick in Vue and vuenexttick

NextTick in Vue involves asynchronous DOM update in Vue, which is very interesting. The source code about nextTick involves a lot of knowledge, but I don't quite understand it. Now I will introduce nextTick based on my own feelings.

I. Example

Let's take an example to learn about DOM update and nextTick functions in Vue.

Template

<div class="app"> <div ref="msgDiv">{{msg}}</div> <div v-if="msg1">Message got outside $nextTick: {{msg1}}</div> <div v-if="msg2">Message got inside $nextTick: {{msg2}}</div> <div v-if="msg3">Message got outside $nextTick: {{msg3}}</div> <button @click="changeMsg">  Change the Message </button></div>

Vue instance

new Vue({ el: '.app', data: {  msg: 'Hello Vue.',  msg1: '',  msg2: '',  msg3: '' }, methods: {  changeMsg() {   this.msg = "Hello world."   this.msg1 = this.$refs.msgDiv.innerHTML   this.$nextTick(() => {    this.msg2 = this.$refs.msgDiv.innerHTML   })   this.msg3 = this.$refs.msgDiv.innerHTML  } }})

Before clicking

After clicking

We can know that the content displayed by msg1 and msg3 is still changed, while the content displayed by msg2 is changed. The root cause is that DOM update in Vue is asynchronous (detailed description is later ).

II. Application scenarios

The following describes the main application scenarios and causes of nextTick.

DOM operations performed by the created () Hook Function in the Vue life cycle must be placed in the callback function of Vue. nextTick ().

The DOM is not rendered when the created () hook function is executed, and DOM operations are useless at this time. Therefore, JavaScript code of DOM operations must be put into Vue. in the callback function of nextTick. The corresponding function is the mounted () hook function, because all DOM mounting and rendering are completed when the hook function is executed, at this time, there will be no problem with any DOM operations in the hook function.

An operation to be executed after the data changes. When this operation requires the DOM structure changed as the data changes, this operation should be put into Vue. in the callback function of nextTick.

The specific cause is detailed in the official Vue documentation:

The Vue executes DOM update asynchronously. Once the data changes are observed, Vue starts a queue and caches all data changes in the same event loop. If a watcher is triggered multiple times, it will only be pushed to the queue once. This removal of duplicate data during buffering is very important to avoid unnecessary computation and DOM operations. Then, in the next event loop "tick", the Vue refreshes the queue and performs the actual (de-duplicated) work. Vue internally tries to use native Promise. then and MessageChannel for asynchronous queues. If the execution environment does not support this, it will use setTimeout (fn, 0) instead.

For example, if you set vm. someData = 'new value', this component will not be re-rendered immediately. When the queue is refreshed, the component will update the next "tick" when the event loop queue is cleared. In most cases, we do not need to care about this process, but it may be tricky if you want to do something after the DOM status update. Although Vue. js usually encourages developers to think in a "data-driven" way to avoid direct access to DOM, sometimes we do. To wait for the Vue to complete DOM update after the data changes, you can use Vue. nextTick (callback) immediately after the data changes ). In this way, the callback function is called after the DOM update is complete.

Iii. Analysis of nextTick source code

Function

Vue. nextTick is used to delay the execution of a piece of code. It accepts two parameters (the Context Environment of the callback function and the callback function). If the callback function is not provided, the promise object is returned.

Source code

/** * Defer a task to execute it asynchronously. */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]()  } } // the nextTick behavior leverages the microtask queue, which can be accessed // via either native Promise.then or MutationObserver. // MutationObserver has wider support, however it is seriously bugged in // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It // completely stops working after triggering a few times... so, if native // Promise is available, we will use it: /* istanbul ignore if */ if (typeof Promise !== 'undefined' && isNative(Promise)) {  var p = Promise.resolve()  var logError = err => { console.error(err) }  timerFunc = () => {   p.then(nextTickHandler).catch(logError)   // in problematic UIWebViews, Promise.then doesn't completely break, but   // it can get stuck in a weird state where callbacks are pushed into the   // microtask queue but the queue isn't being flushed, until the browser   // needs to do some other work, e.g. handle a timer. Therefore we can   // "force" the microtask queue to be flushed by adding an empty timer.   if (isIOS) setTimeout(noop)  } } else if (!isIE && typeof MutationObserver !== 'undefined' && (  isNative(MutationObserver) ||  // PhantomJS and iOS 7.x  MutationObserver.toString() === '[object MutationObserverConstructor]' )) {  // use MutationObserver where native Promise is not available,  // e.g. PhantomJS, iOS7, Android 4.4  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 {  // fallback to setTimeout  /* istanbul ignore next */  timerFunc = () => {   setTimeout(nextTickHandler, 0)  } } return function queueNextTick (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   timerFunc()  }  if (!cb && typeof Promise !== 'undefined') {   return new Promise((resolve, reject) => {    _resolve = resolve   })  } }})()

First, understand the three important variables defined in nextTick.

  1. Callbacks: used to store all the callback functions to be executed
  2. Pending: Used to indicate whether the callback function is being executed
  3. TimerFunc: used to trigger the execution of the callback function

Next, let's take a look at the nextTickHandler () function.

function nextTickHandler () {  pending = false  const copies = callbacks.slice(0)  callbacks.length = 0  for (let i = 0; i < copies.length; i++) {   copies[i]()  } }

This function is used to execute all the callback functions stored in callbacks.

Next, assign the trigger method to timerFunc.

First, determine whether the native supports promise. If yes, use promise to trigger the execution of the callback function;

Otherwise, if MutationObserver is supported, an observer object is instantiated. When the text node changes, all callback functions are triggered.

If none of them are supported, use setTimeout to set the latency to 0.

The last is the queueNextTick function. Because nextTick is an instant function, the queueNextTick function is a returned function that accepts user-passed parameters and stores them in callbacks.

Is the entire execution process, the key lies in timeFunc (), this function plays the role of delayed execution.

From the above introduction, we can know that there are three implementation methods for timeFunc.

  1. Promise
  2. MutationObserver
  3. SetTimeout

Among them, Promise and setTimeout are well understood. They are an asynchronous task that calls back a specific function after a synchronization task and an asynchronous task that updates the DOM.

The following describes MutationObserver.

MutationObserver is a new API in html5. it is an interface used to monitor DOM changes. It can listen to subnode deletion, attribute modification, and text content modification on a DOM object.

The call process is simple, but it is a bit unusual: You need to bind the callback to him first:

var mo = new MutationObserver(callback)

You can pass a callback to the MutationObserver constructor to obtain a MutationObserver instance, which is triggered when the MutationObserver instance listens for changes.

At this time, you only bound the callback to the MutationObserver instance. The specific DOM, listening Node Deletion, and listening attribute modification are not set yet. You can call the observer method to complete this step:

Var domTarget = the dom node mo. observe you want to listen to (domTarget, {characterData: true // modify the listening text content .})

The role of MutationObserver in nextTick is shown in. After listening for DOM update, call the callback function.

In fact, the reason for using MutationObserver is that nextTick wants an asynchronous API to execute the asynchronous callback that I want to execute after the current synchronous code is executed, including Promise and setTimeout. The depth also involves microtask and other content. If you do not understand it for the time being, you will not go into detail.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.