ホームページ  >  記事  >  ウェブフロントエンド  >  Vue の nextTick にはどのような機能がありますか? nextTickの簡単な実装

Vue の nextTick にはどのような機能がありますか? nextTickの簡単な実装

不言
不言転載
2018-11-24 14:31:405969ブラウズ

この記事では、Vue の nextTick の機能について説明します。どのように達成するか? 、特定の参考値があり、困っている友人がそれを参照できます。お役に立てば幸いです。

nextTick は Vue のコア関数であり、nextTick は Vue の内部実装でよく使用されます。しかし、多くの初心者は nextTick の原理、さらには nextTick の機能さえ理解していません。

それでは、まず nextTick とは何かを見てみましょう。

nextTick 関数

公式ドキュメントの説明を参照してください:

次の DOM 更新サイクルが終了した後に遅延コールバックを実行します。データを変更した直後にこのメソッドを使用して、更新された DOM を取得します。

公式の例をもう一度見てください:

// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
  })

2.1.0 からの新機能: コールバックが提供されず、Promise をサポートする環境では、Promise が返されます。 Vue には Promise ポリフィルが付属していないことに注意してください。そのため、ターゲット ブラウザが Promise をネイティブにサポートしていない場合 (つまり、なぜ私を見ているのですか)、自分でポリフィルを提供する必要があります。

ご覧のとおり、nextTick の主な機能はデータを変更し、コールバック関数を dom の更新に作用させることです。これを見て、多くの人は混乱します。データを変更した後、DOM は自動的に更新されないのはなぜですか?

これは JS のイベント ループに関連しています。オンライン チュートリアルは無数にあるため、ここでは詳しく説明しません。この記事を読み続ける前に、イベント ループを理解することをお勧めします。

実際的な例:

ページャーを備えた表があり、ページをめくるたびに最初の項目を選択する必要があります。通常の状況では、ページめくりをクリックし、バックグラウンドからデータを取得し、テーブル データを更新し、テーブル API を操作して最初の項目を選択することが必要です。

ただし、テーブルのデータは更新されていますが、最初の項目が選択されていないことがわかります。最初の項目を選択すると、データは更新されますが、DOM は更新されないためです。この時点で、DOM が更新された後、nextTick を使用してテーブル内の最初の項目の選択を操作できます。

それでは、DOM が更新された後に nextTick はコールバック関数を実行するために正確に何をするのでしょうか?

ソースコード分析

nextTick のソースコードは src/core/util/next-tick.js にあり、合計 118 行で非常に短く簡潔です。初めてソースコードを読む学生に非常に適しています。

nextTick のソース コードは主に 2 つの部分に分かれています:

1. 機能の検出

#2. 機能の検出に応じてさまざまな方法でコールバック キューを実行します

能力検出

この部分は実際には非常に単純です。ご存知のとおり、イベント ループはマクロ タスクとマイクロ タスクのどちらが実行されるかに関係なく、完了後の次のティックでは、ティック間で UI レンダリングが実行されます。

ただし、マクロ タスクはマイクロ タスクよりも時間がかかるため、ブラウザがサポートしている場合は、最初にマイクロ タスクを使用してください。ブラウザがマイクロタスクをサポートしていない場合は、マクロタスクを使用します。ただし、さまざまなマクロタスクの効率も異なるため、ブラウザのサポートに基づいて異なるマクロタスクを使用する必要があります。

nextTick は、能力テストに関してはこの考えに従っています。

// Determine (macro) task defer implementation.
// Technically setImmediate should be the ideal choice, but it's only available
// in IE. The only polyfill that consistently queues the callback after all DOM
// events triggered in the same loop is by using MessageChannel.
/* istanbul ignore if */
// 如果浏览器不支持Promise,使用宏任务来执行nextTick回调函数队列
// 能力检测,测试浏览器是否支持原生的setImmediate(setImmediate只在IE中有效)
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // 如果支持,宏任务( macro task)使用setImmediate
  macroTimerFunc = () => {
    setImmediate(flushCallbacks)
  }
  // 同上
} else if (typeof MessageChannel !== 'undefined' && (
  isNative(MessageChannel) ||
  // PhantomJS
  MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
  const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = () => {
    port.postMessage(1)
  }
} else {
  /* istanbul ignore next */
  // 都不支持的情况下,使用setTimeout
  macroTimerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

まず、ブラウザが setImmediate をサポートしているかどうかを確認します。サポートしていない場合は、setTimeout のみを使用します。これは最も効率的ではありませんが、最も互換性があります。

その後、ブラウザが Promise をサポートしているかどうかを確認します。サポートしている場合は、Promise を使用してコールバック関数キューを実行します。結局のところ、マイクロタスクはマクロタスクよりも高速です。サポートされていない場合は、マクロ タスクを使用してコールバック関数キューを実行することしかできません。

コールバック関数キューを実行する

コールバック関数キューを実行するコードは最初と最後にあります

// 回调函数队列
const callbacks = []
// 异步锁
let pending = false

// 执行回调函数
function flushCallbacks () {
  // 重置异步锁
  pending = false
  // 防止出现nextTick中包含nextTick时出现问题,在执行回调函数队列前,提前复制备份,清空回调函数队列
  const copies = callbacks.slice(0)
  callbacks.length = 0
  // 执行回调函数队列
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

...

// 我们调用的nextTick函数
export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  // 将回调函数推入回调队列
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, &#39;nextTick&#39;)
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  // 如果异步锁未锁上,锁上异步锁,调用异步函数,准备等同步函数执行完后,就开始执行回调函数队列
  if (!pending) {
    pending = true
    if (useMacroTask) {
      macroTimerFunc()
    } else {
      microTimerFunc()
    }
  }
  // $flow-disable-line
  // 2.1.0新增,如果没有提供回调,并且支持Promise,返回一个Promise
  if (!cb && typeof Promise !== &#39;undefined&#39;) {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

全体のプロセスは、コールバック関数を受け取り、コールバック関数をコールバック関数キューの中央にプッシュします。

同時に、最初のコールバック関数を受信したときに、機能テストで対応する非同期メソッドを実行します (コールバック関数キューは非同期メソッドで呼び出されます)。

最初のコールバック関数を受信したときにのみ非同期メソッドが実行されるようにするにはどうすればよいですか?

nextTick ソース コードでは、非同期ロックの概念が使用されています。つまり、最初のコールバック関数を受信したときに、まずロックを閉じて、非同期メソッドを実行します。このとき、ブラウザは非同期コードを実行する前に、同期コードが実行されるのを待っています。

これは、グループの乗客がバスに乗車する準備をしていることに相当します。最初の乗客が乗車すると、バスは発車し、全員が乗車した後に出発することに相当します。バスが正式に運転を開始できるようになります。

もちろん、flushCallbacks 関数を実行するときに理解するのが難しい点があります。それは、なぜコールバック関数のキューをバックアップする必要があるのか​​ということです。コールバック関数のキューも実行されるのでしょうか?

なぜなら、次のような状況が発生するためです: nextTick は nextTick を適用します。 flashCallbacks が特別な処理を行わず、ループ内でコールバック関数を直接実行する場合、nextTick のコールバック関数はコールバック キューに入ります。これは、前のバスに次のバスに乗車する乗客に相当します。

単純な nextTick の実装

ここまで述べたので、単純な nextTick を実装しましょう:

let callbacks = []
let pending = false

function nextTick (cb) {
    callbacks.push(cb)

    if (!pending) {
        pending = true
        setTimeout(flushCallback, 0)
    }
}

function flushCallback () {
    pending = false
    let copies = callbacks.slice()
    callbacks.length = 0
    copies.forEach(copy => {
        copy()
    })
}

ご覧のとおり、nextTick の単純なバージョンでは、nextTick を通じて、コールバック関数を呼び出し、setTimeout を通じてコールバック関数を非同期に実行します。このようにして、コールバック関数は次のティックで実行できます。つまり、コールバック関数は UI が再レンダリングされた後に実行されます。

以上がVue の nextTick にはどのような機能がありますか? nextTickの簡単な実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。