ホームページ > 記事 > ウェブフロントエンド > Vue.nextTickの実装方法を詳しく解説
この記事では主にVue.nextTickの実装方法を紹介しますので、参考にしてください。編集者をフォローして見てみましょう。皆さんのお役に立てれば幸いです。
これは、イベントループとMicroTaskに続くvue.nextTick API実装のソースコード分析です。
ウォーミングアップしてスリープ関数を作成します
function sleep (ms) { return new Promise(resolve => setTimeout(resolve, ms) } async function oneTick (ms) { console.log('start') await sleep(ms) console.log('end') } oneTick(3000)
スリープ関数について説明します
await PromiseFn() が実行されると、この PromiseFn が microTask 内で実行されることもわかります。 microTaskが実行されていない場合、後続のmacroTaskは実行されません。また、microTaskのイベントループ機能により、console.logの実行を防止します。 ('start')
2 await を実行し、実行は一時停止され、await 関数の後の PromiseFn が microTask で実行されるのを待ちます
4 console.log('end' を実行します) )resolveに戻った後
nextTick API
vueでのnextTickの使い方
vue.nextTick(() => { // todo... })
使い方を理解したら、ソースコードを見てみましょう
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]() // 逐个执行 } } if (typeof Promise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve() var logError = err => { console.error(err) } timerFunc = () => { p.then(nextTickHandler).catch(logError) // 重点 } } else if ('!isIE MutationObserver') { var counter = 1 var observer = new MutationObserver(nextTickHandler) // 重点 var textNode = document.createTextNode(string(conter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } } else { timerFunc = () => { setTimeout(nextTickHandler, 0) // 重点 } } return function queueNextTick (cb, ctx) { // api的使用方式 let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { err } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } if (!cb && typeof Promise !== 'undefined') { return new Promise((resolve, reject) => { _resolve =resolve }) } } })() // 自执行函数ソースコードを見ると、nextTick API が自己実行関数であることがわかります
自己実行関数の場合は、戻り値の型を直接見て、戻り関数 queueNextTick (cb, ctx) {...}
return function queueNextTick (cb, ctx) { // api的使用方式 let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { err } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } if (!cb && typeof Promise !== 'undefined') { return new Promise((resolve, reject) => { _resolve =resolve }) } }メインプロセスの queueNextTick 関数が渡す () のみに注目してください => { // todo ... } コールバックにプッシュされます
if (typeof Promise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve() var logError = err => { console.error(err) } timerFunc = () => { p.then(nextTickHandler).catch(logError) // 重点 } } else if ('!isIE MutationObserver') { var counter = 1 var observer = new MutationObserver(nextTickHandler) // 重点 var textNode = document.createTextNode(string(conter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } } else { timerFunc = () => { setTimeout(nextTickHandler, 0) // 重点 } }このセクションでは、マークされた 3 つのポイントを確認できます。 Promise、MutationObserver、または setTimeout(fn, 0) が、さまざまなブラウザ環境で nextTickHandler
を実行するために使用されることを示します。
function nextTickHandler () { pending = false const copies = callbacks.slice(0) // 复制 callbacks.length = 0 // 清空 for (let i = 0; i < copies.length; i++) { copies[i]() // 逐个执行 } }nextTickHandler は、 => { // todo... } の前にコールバックに入れられた () を実行します。現在のタスク。
簡単な nextTick を書く
ソース コードは複雑かもしれません。簡単な nextTick を自分で書いてみましょう
const simpleNextTick = (function () { let callbacks = [] let timerFunc return function queueNextTick (cb) { callbacks.push(() => { // 给callbacks 推入cb() cb() }) timerFunc = () => { return Promise.resolve().then(() => { const fn = callbacks.shift() fn() }) } timerFunc() // 执行timerFunc,返回到是一个Promise } })() simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') })ここから、nextTick の原則は Promise を返すことであり、todo コードは次のとおりであることがわかります。 in この Promise で実行されたので、引き続き
const simpleNextTick = (function () { return function queueNextTick (cb) { timerFunc = () => { return Promise.resolve().then(() => { cb() }) } timerFunc() } })() simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') })を単純化し、次のように直接書くことができます。
const simpleNextTick = function queueNextTick (cb) { timerFunc = () => { return Promise.resolve().then(() => { cb() }) } timerFunc() } simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') })今回は、自己実行関数も単純化しました
const simpleNextTick = function queueNextTick (cb) { return Promise.resolve().then(cb) } simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') })これで、最後まで直接単純化され、nextTick のコアコンテンツは Promise、つまりマイクロタスクであることがわかりました。 ここで、vue の nextTick API の公式サンプルに戻ります
<p id="example">{{message}}</p> 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 })vue 内のデータが更新された後の dom update は、次のイベントループの後に実行されることがわかります。
nextTick を使用する原理は、主に、単一イベントでデータを更新した直後に DOM を操作するシナリオを解決することです。
nextTick の核心は microTasks を使用することであることがわかったので、簡略化された nextTick と冒頭の sleep 関数を比較してみましょう。
const simpleNextTick = function queueNextTick (cb) { return Promise.resolve().then(cb) } simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') // 也可以换成ajax请求 })
function sleep (ms) { return new Promise(resolve => setTimeout(resolve, ms) // 也可以换成ajax请求 } async function oneTick (ms) { console.log('start') await sleep(ms) console.log('end') } oneTick(3000)
私たちが書いた nextTick と oneTick の実行結果が非常に似ていることがわかります。唯一の違いは、nextTick がコールバックを Promise でラップして返し、実行するのに対し、oneTick は await を使用して Promise 関数を実行し、この Promise には独自のラップされた webapi 関数があることです。
async function getData () { const data = await axios.get(url) // 操作data的数据来改变dom return data }これは nextTick と同じ効果を達成することもできます最後に、ソース コードからもブラウジング時に次のことがわかります。サーバー環境が Promise をサポートしていない場合は、MutationObserver または setTimeout(cb, 0) を使用して同じ効果を実現できます。しかし、究極のコアは microTask です
関連する推奨事項:
Vue + Vuex の詳細な説明 vm.$nextTick インスタンスの使用の詳細な説明
以上がVue.nextTickの実装方法を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。