ホームページ >ウェブフロントエンド >jsチュートリアル >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 です
関連する推奨事項:
Node.js の process.nextTick の使用例
以上がVue.nextTickの実装方法の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。