ホームページ  >  記事  >  ウェブフロントエンド  >  Vue の nextTick メソッドの詳細な紹介

Vue の nextTick メソッドの詳細な紹介

亚连
亚连オリジナル
2018-06-08 17:40:192150ブラウズ

この記事では主に Vue の nextTick メソッドの簡単な理解を紹介し、参考として提供します。 Vue の

nextTick には、Vue の DOM の非同期更新が含まれており、非常に興味深く、特に学びました。 nextTick のソースコードには多くの知識が含まれており、その多くはよく理解できませんが、私自身の知見をもとに nextTick について紹介していきます。

1. 例

まず例を挙げて、Vue での DOM 更新と nextTick の役割について学びましょう。

テンプレート

<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>

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
  }
 }
})

クリック前

クリック後

画像からわかるように、msg1とmsg3で表示される内容はまだ変換前ですが、表示されている内容は変換前です。 by msg2 は変換後です。根本的な理由は、Vue での DOM 更新が非同期であるためです (詳細は以下で説明します)。

2. アプリケーションシナリオ

nextTick の主なアプリケーションシナリオとその理由について学びましょう。

Vue ライフサイクルの created() フック関数で実行される DOM 操作は、Vue.nextTick() のコールバック関数に配置する必要があります

DOM は、created() フック関数の実行時に実際にはまったくレンダリングされません。この時点で DOM 操作を実行するのは無駄に等しいため、ここでは DOM 操作用の JS コードを Vue.nextTick() のコールバック関数に入れる必要があります。これに対応するのが、mounted() フック関数です。フック関数が実行される時点で、すべての DOM のマウントとレンダリングが完了しているため、フック関数内で DOM 操作を実行しても問題ありません。

データ変更後に操作を実行する必要があり、この操作でデータの変更に応じて変更される DOM 構造を使用する必要がある場合、この操作を Vue.nextTick() のコールバック関数に含める必要があります。

具体的な理由は、Vue の公式ドキュメントで詳しく説明されています:

Vue は DOM 更新を非同期的に実行します。データ変更が観察されるとすぐに、Vue はキューを開き、同じイベント ループ内で発生するすべてのデータ変更をバッファーに入れます。同じウォッチャーが複数回トリガーされた場合、キューにプッシュされるのは 1 回だけです。バッファリング中のこの重複排除は、不必要な計算や DOM 操作を回避するために重要です。次に、次のイベント ループ「ティック」で、Vue はキューをフラッシュし、実際の (重複排除された) 作業を実行します。 Vue は内部的に、非同期キューにネイティブの Promise.then と MessageChannel を使用しようとします。実行環境がそれをサポートしていない場合は、代わりに setTimeout(fn, 0) が使用されます。

たとえば、vm.someData = '新しい値' と設定すると、コンポーネントはすぐには再レンダリングされません。キューがフラッシュされると、イベント ループ キューがクリアされる次の「ティック」でコンポーネントが更新されます。ほとんどの場合、このプロセスについて心配する必要はありませんが、DOM 状態が更新された後に何かをしたい場合は、少し注意が必要になることがあります。 Vue.js は通常、開発者が「データ駆動型」の方法で考え、DOM に直接触れないようにすることを推奨していますが、実際にそうする必要がある場合もあります。データ変更後に Vue による DOM の更新が完了するのを待つには、データ変更直後に Vue.nextTick(callback) を使用します。このコールバック関数は、DOM の更新が完了した後に呼び出されます。

3. nextTick ソースコードの簡単な分析

関数

Vue.nextTick は、2 つのパラメーター (コールバック関数とコールバック関数を実行するためのコンテキスト) を受け取ります。コールバック関数が提供されていない場合は、Promise オブジェクトが返されます。

ソースコード

/**
 * 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
   })
  }
 }
})()

まず、nextTick で定義されている 3 つの重要な変数を理解しましょう。

  1. callbacks: 実行する必要があるすべてのコールバック関数を保存するために使用されます

  2. pending: コールバック関数が実行されているかどうかをマークするために使用されます

  3. timerFunc: コールバック関数の実行をトリガーするために使用されます

次に、nextTickHandler()関数について学びます。

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

この関数は、コールバックに格納されているすべてのコールバック関数を実行するために使用されます。

次のステップは、トリガー メソッドを timerFunc に割り当てることです。

まず、Promise がネイティブにサポートされているかどうかを判断します。サポートされている場合は、Promise を使用してコールバック関数の実行をトリガーします。それ以外の場合、MutationObserver がサポートされている場合は、テキスト ノードの変更を監視するときにオブザーバー オブジェクトをインスタンス化し、すべてのコールバック関数の実行をトリガーします。 。

どちらもサポートされていない場合は、setTimeout を使用して遅延を 0 に設定します。

最後はqueueNextTick関数です。 nextTick は即時関数であるため、queueNextTick 関数はユーザーから渡されたパラメーターを受け入れる返される関数であり、コールバック関数をコールバックに格納するために使用されます。

上の図は、実行プロセス全体を示しています。鍵となるのは、実行を遅らせる役割を果たす timeFunc() です。

上記の紹介から、timeFunc() には 3 つの実装メソッドがあることがわかります。

    Promise
  1. MutationObserver
  2. 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如何实现文字间歇循环滚动效果

以上がVue の nextTick メソッドの詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。