Vue の Vue.nextTick を理解するのに役立つ情報を共有する

青灯夜游
リリース: 2022-03-22 11:38:00
転載
2265 人が閲覧しました

この記事では、Vueのピュアな辛口情報と、あなたの知らないVue.nextTickについてご紹介します。

Vue の Vue.nextTick を理解するのに役立つ情報を共有する

Vue を使用したことのある友人は多かれ少なかれそれを知っているでしょう$nextTick~ nextTick を正式に説明する前に、Vue について明確に理解しておく必要があると思います。 DOM を更新するときは、非同期で実行されます。次の説明プロセスはコンポーネントの更新と一緒に説明します~ 早速、本題に入りましょう (この記事では v2.0 を使用します)。 6.14 バージョンの Vue ソース コードを説明します) [関連する推奨事項: vuejs ビデオ チュートリアル ]

1. NextTick クイズ

NextTick を本当に理解していますか?さあ、直接質問に行きましょう~

<template>
  <div id="app">
    <p ref="name">{{ name }}</p>
    <button @click="handleClick">修改name</button>
  </div>
</template>

<script>
  export default {
  name: &#39;App&#39;,
  data () {
    return {
      name: &#39;井柏然&#39;
    }
  },
  mounted() {
    console.log(&#39;mounted&#39;, this.$refs.name.innerText)
  },
  methods: {
    handleClick () {
      this.$nextTick(() => console.log(&#39;nextTick1&#39;, this.$refs.name.innerText))
      this.name = &#39;jngboran&#39;
      console.log(&#39;sync log&#39;, this.$refs.name.innerText)
      this.$nextTick(() => console.log(&#39;nextTick2&#39;, this.$refs.name.innerText))
    }
  }
}
</script>
ログイン後にコピー

上記のコードでは、

が「名前の変更」ボタン をクリックすると、'nextTick1', '同期ログ' 'nextTick2' に対応する this.$refs.name.innerText がそれぞれ出力されますか? 、ここに出力されるのはDOMのinnerTextです~(答えは記事の最後に掲載されます)

現時点で明確な答えがある場合は、読み続ける必要はありません。わかりました~でも、答えに不安がある場合は、私に従って読み続けてください。読めば答えを見なくても明確な答えがわかると思いますよ~!


2. NextTick ソース コードの実装

ソース コードは core/util/next-tick にあります。これは 4 つの部分に分割でき、コード

#1 に直接移動できます。グローバル変数

##callbacks

Queue, pending Status<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">const callbacks = [] // 存放cb的队列 let pending = false // 是否马上遍历队列,执行cb的标志</pre><div class="contentsignin">ログイン後にコピー</div></div>


2.

flushCallbacksコールバックをトラバースして各 cb

function flushCallbacks () {
  pending = false // 注意这里,一旦执行,pending马上被重置为false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]() // 执行每个cb
  }
}
ログイン後にコピー


3.

nextTick の非同期実装 実行環境のサポートの程度に応じて、さまざまな非同期実装戦略を採用する

let timerFunc // nextTick异步实现fn

if (typeof Promise !== &#39;undefined&#39; && isNative(Promise)) {
  // Promise方案
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks) // 将flushCallbacks包装进Promise.then中
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== &#39;undefined&#39; && (
  isNative(MutationObserver) ||
  MutationObserver.toString() === &#39;[object MutationObserverConstructor]&#39;
)) {
  // MutationObserver方案
  let counter = 1
  const observer = new MutationObserver(flushCallbacks) // 将flushCallbacks作为观测变化的cb
  const textNode = document.createTextNode(String(counter)) // 创建文本节点
  // 观测文本节点变化
  observer.observe(textNode, {
    characterData: true
  })
  // timerFunc改变文本节点的data,以触发观测的回调flushCallbacks
  timerFunc = () => { 
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== &#39;undefined&#39; && isNative(setImmediate)) {
  // setImmediate方案
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // 最终降级方案setTimeout
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}
ログイン後にコピー

実際のこの事例は、
    MutationObserver
  • についての理解を深めます。結局のところ、他の 3 つの非同期ソリューションと比較すると、これは誰にとっても最も馴染みのないものになるはずです。
  • script start
    • script end

      はマクロ タスクの最初のラウンドで実行されますが、これは問題ありません

      setTimeout
    • はマクロ タスク実行の次のラウンドに置かれます
    • #MutationObserver
    • はマイクロ タスクであるため、次のようになりますマクロ タスクの現在のラウンドが実行された後に実行されるため、前に実行されます。
    • setTimeout

      結果は以下のようになります。

  • Vue の Vue.nextTick を理解するのに役立つ情報を共有する#4.
  • nextTick
メソッドの実装

cbPromise

メソッド

const observer = new MutationObserver(() => console.log(&#39;观测到文本节点变化&#39;))
const textNode = document.createTextNode(String(1))
observer.observe(textNode, {
    characterData: true
})

console.log(&#39;script start&#39;)
setTimeout(() => console.log(&#39;timeout1&#39;))
textNode.data = String(2) // 这里对文本节点进行值的修改
console.log(&#39;script end&#39;)
ログイン後にコピー
詳細については、pending が何に使用され、どのように機能するかを理解していますか?

  • ケース 1、Tick の同じラウンドで 2 回実行されます $nextTick
  • timerFunc
は 1 回だけ実行されます

<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">export function nextTick (cb?: Function, ctx?: Object) { let _resolve // 往全局的callbacks队列中添加cb callbacks.push(() =&gt; { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, &amp;#39;nextTick&amp;#39;) } } else if (_resolve) { // 这里是支持Promise的写法 _resolve(ctx) } }) if (!pending) { pending = true // 执行timerFunc,在下一个Tick中执行callbacks中的所有cb timerFunc() } // 对Promise的实现,这也是我们使用时可以写成nextTick.then的原因 if (!cb &amp;&amp; typeof Promise !== &amp;#39;undefined&amp;#39;) { return new Promise(resolve =&gt; { _resolve = resolve }) } }</pre><div class="contentsignin">ログイン後にコピー</div></div> 画像を使用した方が直感的ですか?

  • #3. Vue コンポーネントの非同期更新

Vue のコンポーネント化がある場合は Vue の Vue.nextTick を理解するのに役立つ情報を共有する# # ディストリビューションのアップデート

あまり詳しくない方は、
ここをクリックして Vue の応答性の原理を図解でご覧ください

Vue のコンポーネント化とディストリビューションのアップデートについて学んでから戻ってきてください~

Vue の 非同期更新DOM は、実際には nextTick を使用して実装されます。これは、実際には通常使用する $nextTick と同じです~ここで、何が起こるかを確認します。属性の値を変更するとき?

上図のディストリビューション更新プロセスに従って、watcher.update の先頭から開始し、例として Rendering Watcher

を取り上げ、

queueWatcher

Vue の Vue.nextTick を理解するのに役立つ情報を共有する

1 と入力します。

queueWatcher は何をしましたか? <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">this.$nextTick(() =&gt; console.log(&amp;#39;nextTick1&amp;#39;)) this.$nextTick(() =&gt; console.log(&amp;#39;nextTick2&amp;#39;))</pre><div class="contentsignin">ログイン後にコピー</div></div>

2.

flushSchedulerQueue は何をしましたか?
// 用来存放Wathcer的队列。注意,不要跟nextTick的callbacks搞混了,都是队列,但用处不同~
const queue: Array<Watcher> = []

function queueWatcher (watcher: Watcher) {
  const id = watcher.id // 拿到Wathcer的id,这个id每个watcher都有且全局唯一
  if (has[id] == null) {
    // 避免添加重复wathcer,这也是异步渲染的优化做法
    has[id] = true
    if (!flushing) {
      queue.push(watcher)
    }
    if (!waiting) {
      waiting = true
      // 这里把flushSchedulerQueue推进nextTick的callbacks队列中
      nextTick(flushSchedulerQueue)
    }
  }
}
ログイン後にコピー

最後に、コンポーネントの非同期更新プロセスを理解するための図

    4. 回帰の質問I
  • nextTick ソース コードの上記の分析を通じて、その謎のベールが明らかになったと考えています。このとき、答えをしっかりと伝えることができなければなりませんので、早速、自分の考えと合っているか、一緒に確認してみましょう!

1、如图所示,mounted时候的innerText是“井柏然”的中文

Vue の Vue.nextTick を理解するのに役立つ情報を共有する

2、接下来是点击按钮后,打印结果如图所示

Vue の Vue.nextTick を理解するのに役立つ情報を共有する

  • 没错,输出结果如下(意不意外?惊不惊喜?)

    • sync log 井柏然

    • nextTick1 井柏然

    • nextTick2 jngboran

  • 下面简单分析一下每个输出:

    this.$nextTick(() => console.log(&#39;nextTick1&#39;, this.$refs.name.innerText))
    this.name = &#39;jngboran&#39;
    console.log(&#39;sync log&#39;, this.$refs.name.innerText)
    this.$nextTick(() => console.log(&#39;nextTick2&#39;, this.$refs.name.innerText))
    ログイン後にコピー

    • sync log:这个同步打印没什么好说了,相信大部分童鞋的疑问点都不在这里。如果不清楚的童鞋可以先回顾一下EventLoop,这里不多赘述了~

    • nextTick1:注意其虽然是放在$nextTick的回调中,在下一个tick执行,但是他的位置是在this.name = 'jngboran'的前。也就是说,他的cb会App组件的派发更新(flushSchedulerQueue)更先进入队列,当nextTick1打印时,App组件还未派发更新,所以拿到的还是旧的DOM值。

    • nextTick2就不展开了,大家可以自行分析一下。相信大家对它应该是最肯定的,我们平时不就是这样拿到更新后的DOM吗?

  • 最后来一张图加深理解

  • Vue の Vue.nextTick を理解するのに役立つ情報を共有する


    写在最后,nextTick其实在Vue中也算是比较核心的一个东西了。因为贯穿整个Vue应用的组件化、响应式的派发更新与其息息相关~深入理解nextTick的背后实现原理,不仅能让你在面试的时候一展风采,更能让你在日常开发工作中,少走弯路少踩坑!好了,本文到这里就暂告一段落了,如果读完能让你有所收获,就帮忙点个赞吧~画图不易、创作艰辛鸭~

    (学习视频分享:vuejs教程web前端

    以上がVue の Vue.nextTick を理解するのに役立つ情報を共有するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
vue
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
関連するチュートリアル
人気のおすすめ
最新のコース
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート