Oh my God, vue is buggy and DOM isn’t refreshing again?

At work, developing with VUE, we often encounter situations where we drive the DOM with data and then manipulate the DOM with no effect. If it is useful to TAB switch plus Echarts display, it is definitely angry want to smash the table. Let’s talk about dom refresh in VUE.

What is DOM asynchronous updating?

An asynchronous update is a vue that uses data to drive the DOM. When the data changes, the DOM does not update immediately, but updates the DOM on the next Tick. Of course, the DOM is manually manipulated in vUE, and the DOM is refreshed immediately. What is data-driven DOM? Vue updates the view automatically as the data changes, without manually manipulating the DOM. Let’s take a quick look at the code

View vue’s DOM refresh asynchronously in the browser

What if you have a problem driving the DOM using data in vUE? Of course, it’s $nextTick. What is a nextTick? Defer the callback until the next DOM update cycle, as stated on the official website. Use the data immediately after you modify it, and then wait for DOM updates. NextTick is the same as the global method vue.nexttick, except that the this callback is automatically bound to the instance calling it. In layman’s terms, methods in nextTick fire when the current DOM is updated.

The following code, of course, is very easy to explain.

Vue asynchronous refresh, used to promise, MutationObserver, these three apis and setTimeout, really understand the three API, is the need to be familiar with the browser, if the browser eventloop and microtask macrotask also not familiar with, Please turn left and come back here later. From browser multi process to JS single thread, JS running mechanism is the most comprehensive combing. Don’t forget to come back for more. The following vUE source code requires this knowledge.

What are the advantages of using asynchronous refresh?

When it comes to vUE asynchronous updates, we have to talk about vDOM (virtual DOM). What is VDOM? A virtual DOM is a javascript object that has a number of DOM properties inside it to simulate a real DOM object. The DOM operated in the VUE is the VDOM. After all the DOM is completed, mount the VDOM to the real DOM to reduce the operation on the real DOM. I won’t go into too much detail here.

What are the advantages of asynchronous refreshing? Look at the code below.

If the DOM is refreshed asynchronously in a data-driven way provided by vUE, the ADD () method does not refresh the DOM immediately after numberShu changes, but after the end of the for loop and the final value is obtained, the DOM is updated once. Dom redraw and reflow only happen once. With computedNum(), each for loop triggers a view update, resulting in multiple DOM redraws and reflows. If you change the computedNum() method to this

 computedNum() {
     let m = 0;
       for(leti = 0; i < 100 ; i ++) { m = i; } this.$refs.computedNum = m;
    }

Copy the code

If you rewrite it like this, the DOM will refresh only once. Rather than vue using VDOM diff algorithm for calculation, and then update dom, performance is better. As for the vDOM design concept of VUE, in fact, the user in the case of not considering the performance optimization, for the user to carry out reasonable optimization, does not guarantee that the DOM operation in VUE is the best choice, just let the user develop more cool.

Vue asynchronously updates dom’s strategy, as well as nextTick

When it comes to vUE’s asynchronous DOM update strategy, take a look at how nextTick works.

Implementation principle of nextTick

let callbacks = [];
let pending = false;
let timerFunc



if(typeof Promise ! = =Const p = promise.resolve (); timerFunc = () => { p.then(flushCallbacks) } } else if(typeof MutationObserver ! = = 'undefined') { let counter = 1; const observe = new MutationObserver(flushCallbacks); const textNode = document.createTextNode(String(counter)); observe.observe(textNode,{ characterData: true }) timerFunc = () => { conter = (conter + 1) % 2; textNode.data = String(counter) } } else { timerFunc = () => { setTimeout(flushCallbacks,0) } } function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; I ++) {copies[I]()}} function nextTick(cb, CTX) { Callback.push (() => {if(cb) {cb.call(CTX)} else if(_resolve) {_resolve(CTX); } }) if(! pending) { pending = true; timerFunc(); } if(! cb && type Promise ! = = 'undefined) {
       return new Promise(resolve => {
           _resolve = resolve
       })
   }
}

Copy the code

The three most important parts of implementing nextTick have been removed and simplified. If you are interested, you can go to the source code to see the full version. 1. First nextTick needs to pass in a callback function. In the current stack, when the nextTick method is called for the first time, the callbacks push the callback and its pending value is false by default. P.chen asynchronously executes the flushCallbacks function, which executes the callbacks array. If the nextTick function is called a second time in the current stack, the callbacks continue to be pushed into the callbacks. TimerFunc is never called again because it executes asynchronously in flushCallbacks. A new callback function is pushed into the callbacks array before the callbacks are executed. And so on. FlushCallbacks are executed to change the pending state for the next queue. At this point, we have n callbacks in the callbacks array, and we execute these functions. 2. Which method is used to perform asynchronous execution? Judging from the above, use promise if there is a promise, MutationObserver if not, and setTimeout if not. IE: What are you looking at me for? Am I pretty?) . A quick note about MutationObserve, h5’s new API, which triggers a callback when the DOM changes. Like promise, it’s a microtask.Click me to see the MutationObserver API. 3. According to the source code, nextTick can also be used as a promise. this.NexTick () cannot pass functions.

Asynchronous updates to the DOM

When it comes to VUE, we should definitely overwrite the GET and set methods via defineProperty to achieve bidirectional data binding. After all, one of the most important words in a job interview. Let’s talk about how the DOM is updated asynchronously. When the reactive data in the VUE changes, the Set method calls the Update () method of the Watch class, which in turn calls the queueWatcher method to update the view. See the definition of queueWatcher

let waiting = false;
let index = 0;
lethas = {}; . .export functionQueueWatcher (watcher) {// An instance of watch const id = watcher.id // Each responsive data has a separate watch.idif(has[id] == null) {// If a reactive data changes several times, it is pushed only once to the queue array, and the view refresh only updates the final value. (Remember the add() method above, numberShu is updated only once) has[id] =true
    queue.push(watcher)
    // queue the flush
    if(! waiting) { waiting =true

      if(process.env.NODE_ENV ! = ='production'&&! Config.async) {// Update the DOM if it is not set to async. FlushSchedulerQueue () flushSchedulerQueue()return
      }
      nextTick(flushSchedulerQueue)
    }
  }
}
function flushSchedulerQueue () {
  flushing = true
  let watcher, id

  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    watcher.run()
}
Copy the code

The above code is also simplified code, easy to understand, if interested in the original, you can go to see vUE source code.

1. QueueWatcher adds a view update callback to the queue after the first reactive data change, not if the queue already has the instance. FlushSchedulerQueue iterates through the queue. QueueWatcher finally calls nextTick(flushSchedulerQueue). NextTick puts the callback into the Callbacks array, which is executed asynchronously. So calbacks will have a function that flushSchedulerQueue. In flushSchedulerQueue, there is another queue. If the second reactive data changes in the current stack, the watch instance is added to the queue that has not yet been iterated, and so on. At the end of the current stack task, the callbacks in the task queue are called. At the end of the flushSchedulerQueue, the callbacks in the queue are iterated through and the watch.run() method is called to update the view.

To put it bluntly, vue asynchronous update, the final calbacks array structure is similar to (fn, fn, flushSchedulerQueue, fn).

In flushSchedulerQueue, there is another queue to be iterated through, like the [fn,fn,fn] array.

❤️ if you see the officer see also, please give a thumbs-up, conveniently point a concern, is the biggest support for me