Make writing a habit together! This is the fourth day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details.

A, usage,

Vue.nextTick( [callback, context] )
Copy the code

A deferred callback is performed after the next DOM update loop ends. Use this method immediately after modifying the data to get the updated DOM.

Why do you need $nextTick

Let’s start with an example

<template> <h1 ref="msgRef">{{message}}</h1> < button@click ="handleChangeMsg"> Click </button </template> <script> export Default {name: 'App', data() {return {message: 'small Y'}}, methods: { handleChangeMsg() { this.message = 'DanDan' console.log(222, this.$refs['msgRef'].textContent) this.$nextTick(() => { console.log(1111, this.$refs['msgRef'].textContent) }) } }, } </script>Copy the code

Vue is executed asynchronously when updating the DOM. As long as it listens for data changes, Vue opens a queue and buffers all data changes that occur in the same event loop. If the same watcher is triggered more than once, it will only be pushed into the queue once. Then, in the next event loop tick, Vue flushs the queue and performs the actual (de-duplicated) work. Vue internally attempts to use native Promise.then, MutationObserver, and setImmediate for asynchronous queues, and setTimeout(fn, 0) instead if the execution environment does not support it.

As is clear from the above example, Vue updates the DOM asynchronously. After modifying the message data, we can’t get the latest DOM immediately, so we need to apply nextTick to handle this.

Third, source code analysis

Before you analyze the source code, you need to understand what an event loop is, and if you don’t already know, you can understand what an event loop is.

NextTick can be found in/SRC /core/util/next-tick.js.

First let’s look at the definition of the nextTick method

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 timerFunc() } // $flow-disable-line if (! cb && typeof Promise ! == 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }Copy the code

We can see that when we execute nextTick, we append cb to callbacks, and then we have a pending, and we can see that on the outer layer we define pending to be false, and when we set pending to false, we execute timerFunc, and we look at timerFunc.

function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } } let timerFunc if (typeof Promise ! == 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS)  setTimeout(noop) } isUsingMicroTask = true } else if (! isIE && typeof MutationObserver ! == 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else  if (typeof setImmediate ! == 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks) } } else { // Fallback to setTimeout. timerFunc = () => { setTimeout(flushCallbacks, 0) } }Copy the code

In the case of the timerFunc function, we see that there are four levels of judgment for its assignment. We know that asynchronous processing involves microtasks and macro tasks. Then, MutationObserver, and setImmediate Mediate mediate are not supported, and setTimeout is used to delay execution of the flushCallbacks with a demoted processing.

For flushCallbacks, it writes simply, first set pending to false, then make a copy of the callbacks and loop through it, setting the length of the callbacks to 0. Going back to pending, it’s really just a flag that executes timerFunc once within an event loop.

Iv. Summary of $nextTick principle

  • We start by adding the callback that calls nextTick to the Callbacks array, which we treat as a queue.
  • There is then an identifier pending, which is intended to execute the timerFunc function only once within an event loop.
  • Since Vue updates DOM asynchronously, we can’t get the latest DOM immediately after we modify the data, so we need nextTick to deal with these asynchronously, so we naturally think of microtasks and macro tasks to deal with them. The source code insidePromise.then,MutationObserver,setImmediateandsetTimeoutTo handle the flushCallbacks, wrap the flushCallbacks function in timerFunc to execute.