ホームページ  >  記事  >  ウェブフロントエンド  >  Vue.nextTickの実装方法の簡単な分析

Vue.nextTickの実装方法の簡単な分析

小云云
小云云オリジナル
2018-01-03 11:17:001341ブラウズ

この記事では主に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 で実行されるのを待ちます

3 sleep 関数内で、戻りまでに ms 遅延します

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 !== &#39;undefined&#39; && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError) // 重点
  }
 } else if (&#39;!isIE MutationObserver&#39;) {
  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 !== &#39;undefined&#39;) {
   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 !== &#39;undefined&#39;) {
   return new Promise((resolve, reject) => {
    _resolve =resolve
   })
  }
 }

メインプロセスの queueNextTick 関数が渡す () のみに注目してください => { // todo ... } コールバックにプッシュされます


 if (typeof Promise !== &#39;undefined&#39; && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError) // 重点
  }
 } else if (&#39;!isIE MutationObserver&#39;) {
  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, &#39;nextTick&#39;)
})

ここから、nextTick の原則は Promise を返すことであり、todo コードは次のとおりであることがわかります。 in この Promise で実行されたので、引き続き


const simpleNextTick = (function () {
 return function queueNextTick (cb) {
  timerFunc = () => {
   return Promise.resolve().then(() => {
    cb()
   })
  }
  timerFunc()
 }
})()

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})

を単純化し、次のように直接書くことができます。


const simpleNextTick = function queueNextTick (cb) {
  timerFunc = () => {
   return Promise.resolve().then(() => {
    cb()
   })
  }
  timerFunc()
 }

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})

今回は、自己実行関数も単純化しました


const simpleNextTick = function queueNextTick (cb) {
   return Promise.resolve().then(cb)
 }

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})

これで、最後まで直接単純化され、nextTick のコアコンテンツは Promise、つまりマイクロタスクであることがわかりました。

ここで、vue の nextTick API の公式サンプルに戻ります


<p id="example">{{message}}</p>
var vm = new Vue({
 el: &#39;#example&#39;,
 data: {
  message: &#39;123&#39;
 }
})
vm.message = &#39;new message&#39; // 更改数据
vm.$el.textContent === &#39;new message&#39; // false
Vue.nextTick(function () {
 vm.$el.textContent === &#39;new message&#39; // 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, &#39;nextTick&#39;) // 也可以换成ajax请求
})

function sleep (ms) {
 return new Promise(resolve => setTimeout(resolve, ms) // 也可以换成ajax请求
}
async function oneTick (ms) {
 console.log(&#39;start&#39;)
 await sleep(ms)
 console.log(&#39;end&#39;)
}
oneTick(3000)

私たちが書いた nextTick と oneTick の実行結果が非常に似ていることがわかります。唯一の違いは、nextTick がコールバックを Promise でラップして返し、実行するのに対し、oneTick は await を使用して Promise 関数を実行し、この Promise には独自のラップされた webapi 関数があることです。

それでは、ajax リクエストを行うときに、axios を直接使用して Promise ライブラリを返すことができますか?


async function getData () {
  const data = await axios.get(url)
  // 操作data的数据来改变dom
  return data
}

これは nextTick と同じ効果を達成することもできます

最後に、ソース コードからもブラウジング時に次のことがわかります。サーバー環境が Promise をサポートしていない場合は、MutationObserver または setTimeout(cb, 0) を使用して同じ効果を実現できます。ただし、最後のコアは microTask です


関連する推奨事項:

Node.js の process.nextTick の使用例


node.js_node.js のタイマー nextTick() と setImmediate() の違い分析

Node.js_node.jsにおけるprocess.nextTickの使用例

以上がVue.nextTickの実装方法の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。