Maison  >  Article  >  interface Web  >  Implémenter une file d'attente de mise à jour asynchrone via nextTick() dans Vuejs

Implémenter une file d'attente de mise à jour asynchrone via nextTick() dans Vuejs

亚连
亚连original
2018-06-14 15:11:361789parcourir

Cet article présente principalement l'analyse du code source de la file d'attente de mise à jour asynchrone nextTick() dans Vuejs. Maintenant, je le partage avec vous et le donne comme référence.

Le site officiel de vue explique cela comme suit :

Le principe réactif en profondeur de la file d'attente de mise à jour asynchrone dans vue2.0

Le site officiel explique comme suit :

Tant qu'après avoir observé les changements de données, Vue ouvrira une file d'attente et mettra en mémoire tampon toutes les modifications de données qui se produisent dans la même boucle d'événements. Si le même observateur est déclenché plusieurs fois, il ne sera placé dans la file d'attente qu'une seule fois. Cette déduplication lors de la mise en mémoire tampon est importante pour éviter les calculs et opérations DOM inutiles. Ensuite, lors de la prochaine boucle d'événement "tick", Vue vide la file d'attente et effectue le travail réel (dédupliqué). Vue essaie en interne d'utiliser Promise.then et MutationObserver natifs pour les files d'attente asynchrones. Si l'environnement d'exécution ne le prend pas en charge, setTimeout(fn, 0) sera utilisé à la place.

Par exemple, lorsque vous définissez vm.someData = 'new value' , le composant ne sera pas restitué immédiatement. Lorsque la file d'attente est vidée, le composant est mis à jour au prochain "tic" lorsque la file d'attente de la boucle d'événements est effacée. La plupart du temps, nous n'avons pas à nous soucier de ce processus, mais si vous souhaitez faire quelque chose après la mise à jour de l'état du DOM, cela peut être un peu délicat. Bien que Vue.js encourage généralement les développeurs à penser de manière « basée sur les données » et à éviter de toucher directement au DOM, il y a des moments où nous devons vraiment le faire. Pour attendre que Vue ait fini de mettre à jour le DOM après les modifications des données, vous pouvez utiliser Vue.nextTick(callback) immédiatement après les modifications des données. Cette fonction de rappel sera appelée une fois la mise à jour du DOM terminée. Par exemple, le prototype de la méthode

Analyse du code source

et les commentaires d'analyse sont les suivants :

var nextTick = (function () {
    var callbacks = []; // 存储需要触发的回调函数
    var pending = false; // 是否正在等待的标识(false:允许触发在下次事件循环触发callbacks中的回调, true: 已经触发过,需要等到下次事件循环)
    var timerFunc; // 设置在下次事件循环触发callbacks的 触发函数

    //处理callbacks的函数
    function nextTickHandler () {
      pending = false;// 可以触发timeFunc
      var copies = callbacks.slice(0);//复制callback
      callbacks.length = 0;//清空callback
      for (var i = 0; i < copies.length; i++) {
        copies[i]();//触发callback回调函数
      }
    }

    //如果支持Promise,使用Promise实现
    if (typeof Promise !== &#39;undefined&#39; && isNative(Promise)) {
      var p = Promise.resolve();
      var logError = function (err) { console.error(err); };
      timerFunc = function () {
        p.then(nextTickHandler).catch(logError);
        // ios的webview下,需要强制刷新队列,执行上面的回调函数
        if (isIOS) { setTimeout(noop); }
      };

      //如果Promise不支持,但是支持MutationObserver(h5新特性,异步,当dom变动是触发,注意是所有的dom都改变结束后触发)
    } else if (typeof MutationObserver !== &#39;undefined&#39; && (
        isNative(MutationObserver) ||
        // PhantomJS and iOS 7.x
        MutationObserver.toString() === &#39;[object MutationObserverConstructor]&#39;
      )) {
      // use MutationObserver where native Promise is not available,
      // e.g. PhantomJS IE11, iOS7, Android 4.4
      var counter = 1;
      var observer = new MutationObserver(nextTickHandler);
      //创建一个textnode dom节点,并让MutationObserver 监视这个节点;而 timeFunc正是改变这个dom节点的触发函数
      var textNode = document.createTextNode(String(counter));
      observer.observe(textNode, {
        characterData: true
      });
      timerFunc = function () {
        counter = (counter + 1) % 2;
        textNode.data = String(counter);
      };
    } else {// 上面两种不支持的话,就使用setTimeout

      timerFunc = function () {
        setTimeout(nextTickHandler, 0);
      };
    }
    //nextTick接受的函数, 参数1:回调函数 参数2:回调函数的执行上下文
    return function queueNextTick (cb, ctx) {
      var _resolve;//用于接受触发 promise.then中回调的函数
      //向回调数据中pushcallback
      callbacks.push(function () {
        //如果有回调函数,执行回调函数
        if (cb) { cb.call(ctx); }
        if (_resolve) { _resolve(ctx); }//触发promise的then回调
      });
      if (!pending) {//是否执行刷新callback队列
        pending = true;
        timerFunc();
      }
      //如果没有传递回调函数,并且当前浏览器支持promise,使用promise实现
      if (!cb && typeof Promise !== &#39;undefined&#39;) {
        return new Promise(function (resolve) {
          _resolve = resolve;
        })
      }
    }
  })();

J'ai expliqué la logique du nextTick( ) fonctionnent dans les commentaires

La raison pour laquelle les trois méthodes ci-dessus de gestion des rappels utilisent la priorité : parce que Promise et MutationObserver sont dans la même boucle d'événements que l'événement déclenché (ils s'exécutent simplement dans la micro file d'attente), mais le La fonction de rappel de setTimeout est exécutée dans la prochaine boucle temporelle.

La raison pour laquelle vous utilisez Promise en premier est que MutationObserver s'arrête après avoir été exécuté pendant un certain temps dans UIWebview d'ios9.3.3 ou supérieur.
Les commentaires dans le code ci-dessus ont pleinement expliqué la logique du code. Compréhension simple : placez le rappel dans la file d'attente. S'il n'a pas encore été exécuté, la fonction de rappel sera déclenchée lors de la prochaine exécution de la boucle d'événement.

Remarque : si vous utilisez nextTick() sans définir la fonction de rappel, mais que vous utilisez Promise pour définir la fonction de rappel, cela ne pointe pas vers l'instance Vue actuelle, mais vers la fenêtre (le mode strict n'est pas défini) ;
Mais d'après l'analyse ci-dessus, nous pouvons voir que le contexte d'exécution est transmis via le premier paramètre de la fonction de rappel dans Promise.then().

Où nextTick() est utilisé

1 C'est une fonction de Vue globale, nous pouvons donc l'appeler directement via vue.

2. Dans le système Vue, les opérations utilisées pour traiter les mises à jour du dom

Il existe un observateur dans Vue, qui est utilisé pour observer les changements dans les données, puis mettre à jour le dom. Nous savions plus tôt que dans Vue, toutes les modifications de données ne déclenchent pas une mise à jour du dom. Au lieu de cela, ces opérations sont mises en cache dans une file d'attente après la fin d'une boucle d'événements, la file d'attente est actualisée et les opérations de mise à jour du dom sont effectuées de manière uniforme.

function queueWatcher (watcher) {
    var id = watcher.id;
    if (has[id] == null) {
      has[id] = true;
      if (!flushing) {
        queue.push(watcher);
      } else {
        // if already flushing, splice the watcher based on its id
        // if already past its id, it will be run next immediately.
        var i = queue.length - 1;
        while (i >= 0 && queue[i].id > watcher.id) {
          i--;
        }
        queue.splice(Math.max(i, index) + 1, 0, watcher);
      }
      // queue the flush
      if (!waiting) {
        waiting = true;
        nextTick(flushSchedulerQueue);
      }
    }
  }

Expliquez brièvement la logique du code ci-dessus, car c'est le code de l'observateur et sera analysé plus tard. La fonction de nextTick() ici est d'actualiser l'opération de mise à jour dom vérifiée par l'observateur à la fin de cette boucle d'événements.

3. Partial Vue déclenche $nextTick() et exécute la logique correspondante après la mise à jour du dom.

Vue.prototype.$nextTick = function (fn) {
  return nextTick(fn, this)// 设置nextTick回调函数的上下文环境是当前Vue实例
};

Ce qui précède est un morceau de code dans renderMinxin, qui est le code pour initialiser le module de rendu.

Résumé

Si nous ne comprenons pas son code, nous aurons des malentendus.

1. nextTick() ne redessinera pas la page actuelle, et il ne sera pas exécuté tant que la page ne sera pas redessinée, mais sera définitivement exécuté après la fin de la boucle d'événements.

2. Le déclenchement de cette méthode n'est pas exécuté une fois la mise à jour de la page terminée. Le premier élément a déjà été mentionné, mais pourquoi les données mises à jour peuvent-elles être obtenues dans cette méthode ? L'élément dom a été modifié lorsque l'observateur a exécuté la file d'attente de vidage, il peut donc être obtenu à ce moment-là.

Exemples qui prouvent le point ci-dessus :

h5 a une méthode requestFrameAnimation(callback), le rappel de cette méthode est appelé avant que la page ne soit redessinée. Par expérimentation, lors de la mise à jour du dom, nextTick() est exécuté avant cette méthode.

Ce qui précède est ce que j'ai compilé pour vous. J'espère que cela vous sera utile à l'avenir.

Articles connexes :

Comment charger dynamiquement des données à l'aide de l'arborescence dans le framework Bootstrap

À propos de l'exemple de code de répertoire de chapitre de génération de site Web

Présentation détaillée de la liaison de données Vue

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn