要約: この記事では、nextTick の役割、使用シナリオ、およびその背後にある原則を分析し、すべての人に役立つことを願っています。
この記事は HUAWEI CLOUD コミュニティから共有されたものです "Vue における nextTick の役割とは? 」、CoderBinによる。
1.ネクストティックとは
公式の定義を見てみましょう。
次の DOM 更新ループが終了した後に、遅延コールバックを実行します。データを変更した直後にこのメソッドを使用して、更新された DOM を取得します。
どういう意味ですか?
DOM の更新時に Vue が非同期で実行されることがわかります。データが変更されると、Vue は非同期更新キューを開き、ビューはキュー内のすべてのデータ変更が完了するのを待ってから、均一に更新する必要があります。
例えば:
HTML 構造
<div id="app"> {{ message }} </div>
vue インスタンスをビルドする
const vm = new Vue({
el: '#app',
data: {
message: '原始值'
}
})
メッセージの修正
this.message = '修改后的值1'
this.message = '修改后的值2'
this.message = '修改后的值3'
この時、ページの最新のDOMノードを取得したいのですが、古い値が取得されてしまいます。
console.log(vm.$el.textContent) // 原始值
これは、メッセージ データが変更されると、vue はすぐに Dom を更新せず、データを変更する操作を非同期操作キューに入れるためです。
同じデータを変更し続けると、非同期操作キューも重複排除されます
同じイベント ループ内のすべてのデータ変更が完了するのを待った後、キュー内のイベントが処理され、DOM が更新されます。
なぜ nexttick があるのか
例えば
{{num}}
for(let i=0; i<100000; i++){
num = i
}
nextTick の更新メカニズムがない場合、num の値が更新されるたびにビューが更新されます (上記のコードはビューを 100,000 回更新します)。本質的には最適化戦略です
2. 使用シーン
データを変更した直後に更新された DOM 構造を取得したい場合は、Vue.nextTick() を使用できます。
- 最初のパラメータは: コールバック関数 (最新の DOM 構造を取得できます)
- 2 番目のパラメーターは次のとおりです。実行関数のコンテキスト
// 修改数据
vm.message = '修改后的值'
// DOM 还没有更新
console.log(vm.$el.textContent) // 原始的值
Vue.nextTick(function () {
// DOM 更新了
console.log(vm.$el.textContent) // 修改后的值
})
コンポーネントで vm.$nextTick() インスタンス メソッドを使用するには、this.$nextTick() を渡すだけでよく、コールバック関数の this は現在の Vue インスタンスに自動的にバインドされます。
this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '修改后的值'
})
$nextTick() は Promise オブジェクトを返します。これは、async/await で同じことを達成するために使用できます。
this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
await this.$nextTick()
console.log(this.$el.textContent) // => '修改后的值'
3. 実現原理
ソースコードの場所: /src/core/util/next-tick.js
callbacks は非同期操作キューです
コールバック関数が追加された後、timerFunc 関数が実行されます。Pending は、同じ時間が 1 回しか実行できないことを識別するために使用されます。
export function nextTick(cb?: Function, ctx?: Object) {
let _resolve;
// cb 回调函数会经统一处理压入 callbacks 数组
callbacks.push(() => {
if (cb) {
// 给 cb 回调函数执行加上了 try-catch 错误处理
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
// 执行异步延迟函数 timerFunc
if (!pending) {
pending = true;
timerFunc();
}
// 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve;
});
}
}
timerFunc 関数の定義は、現在の環境でサポートされているメソッドに応じて、呼び出すメソッドを決定するためのものです。
Promise.then、MutationObserver、setImmediate、setTimeout
上記の方法のいずれかを使用してダウングレードします
export let isUsingMicroTask = false
if (typeof Promise !== 'undefined' && isNative(Promise)) {
//判断1:是否原生支持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]'
)) {
//判断2:是否原生支持MutationObserver
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)) {
//判断3:是否原生支持setImmediate
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
//判断4:上面都不行,直接用setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
マイクロ タスクでもマクロ タスクでも、flushCallbacks で使用されます。
ここで、コールバック内の関数のコピーを作成し、コールバックを空のままにします。
コールバック内の関数を順番に実行します
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
第四に、最終的なまとめ
- コールバック関数をコールバックに入れ、実行を待機します
- 実行関数をマイクロタスクまたはマクロタスクに入れる
- イベントはマイクロタスクまたはマクロ タスクにループし、実行関数はコールバック内のコールバックを順番に実行します。
フォローをクリックして、HUAWEI CLOUDの新技術について初めて学びましょう〜