blog

Vue Framework|Vueの$nextTick原理について語る。

Vue は DOM の更新を非同期で行います。データ変更が発生すると、Vue はキューを開き、同じイベントループ内で発生したすべてのデータ変更をバッファリングします。同じウォッチャーが複数回トリガーさ...

Mar 29, 2020 · 5 min. read
シェア

キーワード: vue $nextTick、非同期更新キュー、マイクロタスク、マクロタスク、EventLoop

目次

  • Vueとは$nextTick
  • $nextTick
  • ソースコード解析
  • 雑多な情報

Vueとは$nextTick

Vueは、DOMを更新するときに非同期で実行します。データ変更が発生すると、Vue はキューを開き、同じイベントループ内で発生したすべてのデータ変更をバッファリングします。同じウォッチャーが複数回トリガーされても、キューにプッシュされるのは一度だけです。このようにバッファから重複を取り除くことは、不要な計算やDOM操作を避けるために重要です。次に、イベントループの次の "tick "で、Vueはキューを更新し、実際の処理を実行します。内部的には、VueはネイティブのPromise.then、MutationObserver、非同期キュー用のsetImmediateを、環境がサポートしていない場合は、非同期キュー用のsetTimeout()を使用しようとします。そして、MutationObserverとsetImmediateは非同期キューに対して内部的に試行され、実行環境がそれらをサポートしていない場合、代わりにsetTimeout(fn, 0)が使用されます。

$nextTick

  • ビューが更新された後、新しいビューに基づいて操作する必要があります。
  • created()フックが実行されている間、DOMは実際にはレンダリングを行わないため、これはcreatedサイクルの間に使用され、実際にはmounted()フックと同等です。
  • 使用法 1:
<div id="example">{{message}}</div>
var vm = new Vue({
 el: '#example',
 data: {
 message: '123'
 }
})
vm.message = 'new message' // データを変更する
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
 vm.$el.textContent === 'new message' // true
})
  • コンポーネント内でvm.$nextTick()インスタンスメソッドを使用すると、グローバルなVueを必要とせず、コールバック関数内の thisが自動的に現在のVueインスタンスにバインドされるため、特に便利です:
Vue.component('example', {
 template: '<span>{{ message }}</span>',
 data: function () {
 return {
 message: ' '
 }
 },
 methods: {
 updateMessage: function () {
 this.message = '更新'
 console.log(this.$el.textContent) // => ' '
 this.$nextTick(function () {
 console.log(this.$el.textContent) // => '更新'
 })
 }
 }
})
  • 使用法2、$nextTick()はPromiseオブジェクトを返すので、ES2017の新しいasync/await構文を使って同じことができます:
methods: {
 updateMessage: async function () {
 this.message = '更新'
 console.log(this.$el.textContent) // => ' '
 await this.$nextTick()
 console.log(this.$el.textContent) // => '更新'
 }
}

ソースコード解析

  • flushCallbacks について
// 同期実行コールバック関数キュー
function flushCallbacks () {
 // 現在の実行状態を非待ち状態に設定する。
 pending = false
 // 浅いコピーコールバックキュー配列
 const copies = callbacks.slice(0)
 // リセット・コールバック・キュー(コールバックに相当する= []
 callbacks.length = 0
 // コールバック関数をループする
 for (let i = 0; i < copies.length; i++) {
 copies[i]()
 }
}
  • timerFunc について
// プラットフォームによって異なる,
// さまざまなバージョン,
// Promiseで優先度を設定する - それぞれ> MutationObserver -> setImmediate -> setTimeout timerFuncにvueのメソッドを割り当てる
// timerFuncが実行されたときにマイクロタスクとして設定するためである。> ブラウザのEventLoopで使用されるマクロ・タスク
if (typeof Promise !== 'undefined' && isNative(Promise)) {
 const p = Promise.resolve()
 timerFunc = () => {
 p.then(flushCallbacks)
 
 if (isIOS) setTimeout(noop)
 }
 isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
 isNative(MutationObserver) ||
 
 MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
 
 let counter = 1
 const observer = new MutationObserver(flushCallbacks)
 const textNode = document.createTextNode(String(counter))
 observer.observe(textNode, {
 characterData: true
 })
 timerFunc = () => {
 counter = (counter + 1) % 2
 textNode.data = String(counter)
 }
 isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
 
 timerFunc = () => {
 setImmediate(flushCallbacks)
 }
} else {
 timerFunc = () => {
 setTimeout(flushCallbacks, 0)
 }
}
  • nextTickについて
// nextTickメソッドをエクスポートして、Vue内部とvm / Vue外部で呼び出せるようにする。
export function nextTick (cb?: Function, ctx?: Object) {
 let _resolve
 // コールバック関数をコールバック・キューに入れる
 callbacks.push(() => {
 if (cb) {
 // コールバック関数のエラーによってJSスレッド全体がハングアップするのを防ぐために、tryを使用する。... catch
 try {
 cb.call(ctx)
 } catch (e) {
 handleError(e, ctx, 'nextTick')
 }
 } else if (_resolve) {
 _resolve(ctx)
 }
 })
 // nextTickが現在アイドル状態でなければ、非アイドル状態に設定し、コールバック関数キューの実行を開始する。
 if (!pending) {
 pending = true
 timerFunc()
 }
 // コールバック関数が渡されない場合、nextTickはPromiseとして返される。
 if (!cb && typeof Promise !== 'undefined') {
 return new Promise(resolve => {
 _resolve = resolve
 })
 }
}

雑多な情報

以下はすべて個人的な意見です。

  • VueのnextTickは、基本的にJavaScriptの実行原理EventLoopの応用です。
  • nextTickのコアは、Promise、MutationObserver、setImmediate、setTimeoutといったJavaScriptのネイティブメソッドを利用して、対応するマイクロ/マクロタスクの実装をシミュレートします。基本的には、JavaScriptでこれらの非同期コールバックタスクキューを利用して、VueフレームワークのVueフレームワーク独自の非同期コールバックキュー
  • nextTickは、非同期キューを呼び出すためのVueの内部メソッドであるだけでなく、開発者が実際のプロジェクトで、実際のアプリケーションのDom更新のタイミングに従うロジックに使用することもできます。
  • nextTickは、JavaScriptの基本的な実行原則を特定のケースに適用した典型的な例です。
  • 非同期の更新キュー機構を導入した理由:
    • 更新が同期的である場合、1 つまたは複数の属性に複数の割り当てがあると、UI/Dom のレンダリングが頻繁にトリガされるため、無駄なレンダリングを減らすことができます。
    • 同時に、VirtualDOM の導入により、状態が変更されるたびに状態変更信号がコンポーネントに送信され、コンポーネントが VirtualDOM を使用して更新が必要な特定の DOM ノードを計算し、DOM を更新します。非同期レンダリングが重要になります。

クリエイティブ・コモンズ・ライセンスを見る



この作品は下に提供されています。

Read next