Usage of $nextTick in vue

Introduction

Vue is a very popular framework that combines the advantages of angular and react to form a lightweight and easy-to-use mvvm framework with two-way data binding features. I prefer to use it. When we use vue, a method we often use is this.$nextTick, I believe you have used it too. My common scenario is that after obtaining data, when I need to perform the next operation or other operations on the new view, I find that the DOM cannot be obtained. Because the assignment operation only completes the data model change and does not complete the view update. At this time we need to use the functions introduced in this chapter.

Why use nextTick

Please see the following piece of code

copy code
new View({
  el: '#app',
  data: {
    list: []
  },
  mounted: function () {
    this.get()
  },
  methods: {
    get: function () {
      this.$http.get('/api/article').then(function (res) {
        this.list = res.data.data.list
        // ref list refers to the ul element, I want to make the first li color red
        this.$refs.list.getElementsByTagName('li')[0].style.color = 'red'
      })
    },
  }
})
copy code

After I get the data, I assign it to the list attribute in the data model, and then I want to refer to the ul element to find the first li and change its color to red, but in fact, this is going to be an error, we know, when executing this sentence At the time, there is no li under ul, that is to say, the assignment operation just performed does not currently cause the update of the view layer. Therefore, in such a case, vue provides us with the $nextTick method. If we want to operate on the updated view in the future, we only need to pass the function to be executed to the this.$nextTick method, and vue will give us do this work.

Source code interpretation

This function is very simple, starting at line 450 of vue2.2.6 version.

First of all, is this function a closure function created using a simple interest pattern or something?

var callbacks = []; // array of cached functions
var pending = false; // is it executing
var timerFunc; // holds the function to be executed

First define some variables for later use, the following is a function

copy code
function nextTickHandler () {
  pending = false;
  // make a copy of the function array
  var copies = callbacks.slice(0);
  // clear the function array
  callbacks.length = 0;
  // execute functions sequentially
  for (var i = 0; i < copies.length; i++) {
    copies[i]();
  }
}
copy code

This function is the actual function called in $nextTick.

Next, Vue divides three cases to delay calling the above function, because the purpose of $nextTick is to delay the incoming function until the dom is updated before using it, so here is the elegant descending order of js to do this. .

1. promise.then delayed call

copy code
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  var p = Promise.resolve();
  var logError = function (err) { console.error(err); };
  timerFunc = function () {
    p.then(nextTickHandler).catch(logError);
    if (isIOS) { setTimeout(noop); }
  };
}
copy code

If the browser supports Promise, then use Promise.then to delay the function call. The Promise.then method can delay the function to the end of the current function call stack, that is, the function call stack calls the function at the end. thereby delaying.

2. MutationObserver listens for changes

copy code
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 = function () {
    counter = (counter + 1) % 2;
    textNode.data = String(counter);
  };
}
copy code

MutationObserver is a newly added function of h5. Its function is to monitor the changes of DOM nodes, and execute the callback function after all DOM changes are completed.

Specifically, there are a few changes to monitor

  • childList: Changes to child elements
  • attributes: changes in attributes
  • characterData: Changes in node content or node text
  • subtree: changes of all subordinate nodes (including child nodes and child nodes of child nodes)

It can be seen that the above code creates a text node to change the content of the text node to trigger changes, because after we update the data model, the dom node will be re-rendered, so we add such a change monitor, Use the change of a text node to trigger the monitoring, and after all the DOM is rendered, execute the function to achieve the effect of our delay.

3.setTimeout delayer

else {
    timerFunc = function () {
      setTimeout(nextTickHandler, 0);
    };
  }

Using the delay principle of setTimeout, setTimeout(func, 0) will delay the func function to the beginning of the next function call stack, that is, execute the function after the current function is executed, thus completing the delay function.

Closure function

copy code
  return function queueNextTick (cb, ctx) {
    var _resolve;
    callbacks.push(function () {
      if (cb) { cb.call(ctx); }
      if (_resolve) { _resolve(ctx); }
    });
    // Only execute if no function queue is executing
    if (!pending) {
      pending = true;
      timerFunc ();
    }
    // promise化
    if (!cb && typeof Promise !== 'undefined') {
      console.log('come in')
      return new Promise(function (resolve) {
        _resolve = resolve;
      })
    }
  }
copy code

This return function is the closure function we actually use. Every time we add a function, we want to push the callbacks function array onto the stack. Then monitor whether it is currently executing, and if not, execute the function. This is easy to understand. The next if is a promise.

copy code
this.$nextTick(function () {

})
// promise化
this.$nextTick().then(function () {

}.bind(this))

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325212342&siteId=291194637