Maison  >  Article  >  interface Web  >  Introduction détaillée à la méthode nextTick dans Vue

Introduction détaillée à la méthode nextTick dans Vue

亚连
亚连original
2018-06-08 17:40:192222parcourir

Cet article présente principalement une compréhension simple de la méthode nextTick dans Vue. Maintenant, je la partage avec vous et vous donne une référence.

nextTick dans Vue implique une mise à jour asynchrone du DOM dans Vue. Je trouve cela très intéressant et je l'ai appris spécialement. Le code source de nextTick implique beaucoup de connaissances, dont beaucoup que je ne comprends pas très bien. Présentons nextTick en nous basant sur certaines de mes propres idées.

1. Exemple

Prenons d'abord un exemple pour en savoir plus sur les mises à jour du DOM dans Vue et le rôle de nextTick.

Modèle

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

Instance Vue

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

Avant le clic

Après le clic

Comme le montre l'image : le contenu affiché par msg1 et msg3 est avant la transformation, tandis que le contenu affiché par msg2 est après la transformation. La raison fondamentale est que les mises à jour du DOM dans Vue sont asynchrones (explication détaillée ci-dessous).

2. Scénarios d'application

Apprenons les principaux scénarios d'application et les raisons de nextTick.

Les opérations DOM effectuées dans la fonction hook créée() du cycle de vie de Vue doivent être placées dans la fonction de rappel de Vue.nextTick()

En fait, lorsque la fonction hook créée() est exécuté, le DOM Aucun rendu n'a été effectué et les opérations DOM pour le moment sont vaines, donc le code js pour les opérations DOM doit être placé dans la fonction de rappel de Vue.nextTick(). La fonction hook Mounted() correspond à cela, car tous les montages et rendus DOM sont terminés lorsque la fonction hook est exécutée. À ce stade, il n'y aura aucun problème pour effectuer des opérations DOM dans la fonction hook.

Lorsqu'une opération doit être effectuée après les modifications des données et que cette opération nécessite l'utilisation d'une structure DOM qui change à mesure que les données changent, cette opération doit être placée dans la fonction de rappel de Vue.nextTick() .

La raison spécifique est expliquée en détail dans la documentation officielle de Vue :

Vue effectue les mises à jour du DOM de manière asynchrone. Chaque fois qu'un changement de données est observé, 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 MessageChannel 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.

3. Brève analyse du code source de nextTick

Fonction

Vue.nextTick est utilisée pour retarder l'exécution d'un morceau de code. Il accepte 2 paramètres (la fonction de rappel et le contexte dans lequel la fonction de rappel est exécutée). Si aucune fonction de rappel n'est fournie, un objet de promesse sera renvoyé.

Code source

/**
 * 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 !== &#39;undefined&#39; && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError)
   // in problematic UIWebViews, Promise.then doesn&#39;t completely break, but
   // it can get stuck in a weird state where callbacks are pushed into the
   // microtask queue but the queue isn&#39;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 !== &#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, 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, &#39;nextTick&#39;)
    }
   } else if (_resolve) {
    _resolve(ctx)
   }
  })
  if (!pending) {
   pending = true
   timerFunc()
  }
  if (!cb && typeof Promise !== &#39;undefined&#39;) {
   return new Promise((resolve, reject) => {
    _resolve = resolve
   })
  }
 }
})()

Tout d'abord, comprenons les trois variables importantes définies dans nextTick.

  1. callbacks : utilisé pour stocker toutes les fonctions de rappel qui doivent être exécutées

  2. en attente : utilisé pour marquer si la fonction de rappel est en cours d'exécution

  3. timerFunc : utilisé pour déclencher l'exécution de la fonction de rappel

Ensuite, découvrez la fonction nextTickHandler().

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

Cette fonction est utilisée pour exécuter toutes les fonctions de rappel stockées dans les rappels.

L'étape suivante consiste à attribuer la méthode de déclenchement à timerFunc.

Déterminez d'abord si la promesse est supportée nativement. Si c'est le cas, utilisez promise pour déclencher l'exécution de la fonction de rappel

Sinon, si MutationObserver est pris en charge, instanciez un objet observateur et observez les changements dans le fichier ; nœud de texte Lorsque, toutes les fonctions de rappel sont déclenchées pour être exécutées.

Si aucun des deux n'est pris en charge, utilisez setTimeout pour définir le délai sur 0.

Enfin, la fonction queueNextTick. Étant donné que nextTick est une fonction immédiate, la fonction queueNextTick est une fonction renvoyée qui accepte les paramètres transmis par l'utilisateur et est utilisée pour stocker les fonctions de rappel dans les rappels.

L'image ci-dessus représente l'ensemble du processus d'exécution. La clé réside dans timeFunc(), qui joue le rôle d'exécution retardée.

D'après l'introduction ci-dessus, nous pouvons savoir qu'il existe trois méthodes d'implémentation de timeFunc().

  1. Promesse

  2. MutationObserver

  3. setTimeout

其中Promise和setTimeout很好理解,是一个异步任务,会在同步任务以及更新DOM的异步任务之后回调具体函数。

下面着重介绍一下MutationObserver。

MutationObserver是HTML5中的新API,是个用来监视DOM变动的接口。他能监听一个DOM对象上发生的子节点删除、属性修改、文本内容修改等等。

调用过程很简单,但是有点不太寻常:你需要先给他绑回调:

var mo = new MutationObserver(callback)

通过给MutationObserver的构造函数传入一个回调,能得到一个MutationObserver实例,这个回调就会在MutationObserver实例监听到变动时触发。

这个时候你只是给MutationObserver实例绑定好了回调,他具体监听哪个DOM、监听节点删除还是监听属性修改,还没有设置。而调用他的observer方法就可以完成这一步:

var domTarget = 你想要监听的dom节点
mo.observe(domTarget, {
   characterData: true //说明监听文本内容的修改。
})

在nextTick中 MutationObserver的作用就如上图所示。在监听到DOM更新后,调用回调函数。

其实使用 MutationObserver的原因就是 nextTick想要一个异步API,用来在当前的同步代码执行完毕后,执行我想执行的异步回调,包括Promise和 setTimeout都是基于这个原因。其中深入还涉及到microtask等内容,暂时不理解,就不深入介绍了。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

如何实现vue2.0响应式(详细教程)

详细讲解React中的refs(详细教程)

通过JS如何实现文字间歇循环滚动效果

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