この記事では主に、Vuejs の nextTick() 非同期更新キューのソース コード分析を紹介します。
Vue 公式 Web サイトでは、これについて次のように説明されています:
Vue2.0 の非同期更新キューの徹底的なリアクティブ原理
公式 Web サイトでは次のように説明されています:
データの変更が観察される限り、Vue はキューを開き、同じイベント ループ内で発生するすべてのデータ変更。同じウォッチャーが複数回トリガーされた場合、キューにプッシュされるのは 1 回だけです。バッファリング中のこの重複排除は、不必要な計算や DOM 操作を回避するために重要です。次に、次のイベント ループ「ティック」で、Vue はキューをフラッシュし、実際の (重複排除された) 作業を実行します。 Vue は内部的に、非同期キューにネイティブの Promise.then と MutationObserver を使用しようとします。実行環境がサポートしていない場合は、代わりに setTimeout(fn, 0) が使用されます。
たとえば、 vm.someData = 'new value' と設定すると、コンポーネントはすぐには再レンダリングされません。キューがフラッシュされると、イベント ループ キューがクリアされる次の「ティック」でコンポーネントが更新されます。ほとんどの場合、このプロセスについて心配する必要はありませんが、DOM 状態が更新された後に何かをしたい場合は、少し注意が必要になることがあります。 Vue.js は通常、開発者が「データ駆動型」の方法で考え、DOM に直接触れないようにすることを推奨していますが、実際にそうする必要がある場合もあります。データ変更後に Vue が DOM の更新を完了するのを待つには、データ変更直後に Vue.nextTick(callback) を使用します。このコールバック関数は、DOM の更新が完了した後に呼び出されます。たとえば、
ソースコード解析
メソッドのプロトタイプと解析コメントは以下の通りです:
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 !== 'undefined' && 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 !== 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // 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 !== 'undefined') { return new Promise(function (resolve) { _resolve = resolve; }) } } })();
nextTick()関数のロジックはコメントで説明しました
上記の3つのコールバック処理方法が使用する理由優先順位: Promise と MutationObserver およびトリガーされたイベントは同じイベント ループ内にあるため (ただし、これらはマイクロ キュー内で実行されます)、setTimeout のコールバック関数は次回のタイム ループで実行されるためです。
最初にPromiseを使う理由は、ios9.3.3以降のUIWebviewではMutationObserverが一定時間実行すると停止してしまうためです。
上記のコードのコメントは、コードのロジックを完全に説明しています。簡単に理解すると、コールバックがまだ実行されていない場合は、コールバック関数が次のイベント ループの実行中にトリガーされます。
注: コールバック関数を設定せずに nextTick() を使用し、Promise を使用してコールバック関数を設定する場合、これは現在の Vue インスタンスではなくウィンドウを指します (厳密モードは未定義です)。上記の分析は、実行コンテキストが Promise.then() のコールバック関数の最初のパラメーターを介して渡されることを示しています。
nextTick() を使用します
1. これはグローバル Vue の関数なので、vue から直接呼び出すことができます。 2. Vue システムでは、dom 更新操作を処理するために使用されます。 Vue には、データの変更を監視して dom を更新するために使用されるウォッチャーがあります。 Vue でのすべてのデータ変更が dom の更新をトリガーするわけではなく、イベント ループが終了するとキューにキャッシュされ、dom 更新操作が均一に実行されることは以前からわかっていました。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); } } }
Vue.prototype.$nextTick = function (fn) { return nextTick(fn, this)// 设置nextTick回调函数的上下文环境是当前Vue实例 };
まとめ
コードを理解していないと、誤解が生じてしまいます。 1. nextTick() は現在のページを再描画せず、ページが再描画されるまで実行されませんが、イベント ループが終了した後は必ず実行されます。 2. 最初の項目はすでに述べましたが、なぜこのメソッドで更新されたデータが取得できるのかというと、dom要素の属性がすでに設定されているからです。ウォッチャーでフラッシュされたため、この時点で取得できます。上記の点を証明する例:
h5 には requestFrameAnimation(callback) メソッドがあり、このメソッドのコールバックはページが再描画される前に呼び出されます。実験により、dom を更新するときに、このメソッドの前に nextTick() が実行されます。 上記は私があなたのためにまとめたものです。 関連記事:Bootstrap フレームワークでツリービューを使用してデータを動的にロードする方法
以上がVuejs の nextTick() を使用して非同期更新キューを実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。