Rumah >hujung hadapan web >View.js >Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue

Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue

青灯夜游
青灯夜游ke hadapan
2022-03-22 11:37:122305semak imbas

Artikel ini akan berkongsi dengan anda Vue maklumat tulen dan memperkenalkan anda kepada Vue.nextTick yang anda tidak tahu saya harap ia akan membantu semua orang.

Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue

Rakan yang telah menggunakan Vue akan mengetahui lebih kurang $nextTick~ Sebelum menerangkan secara rasmi nextTick, saya rasa anda harus tahu dengan jelas bahawa Vue sedang mengemas kini DOM. 异步Pelaksanaan, kerana proses penjelasan seterusnya akan diterangkan bersama-sama kemas kini komponen~ Tanpa berlengah lagi, mari terus ke topik (artikel ini menggunakan kod sumber Vue versi v2.6.14 untuk penjelasan ) [Cadangan berkaitan: tutorial video vuejs]

1 kuiz nextTick

Adakah anda benar-benar faham nextTick? Ayuh, pergi terus ke soalan ~

<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>

Dalam kod di atas, apabila mengklik butang "Ubah suai nama", 'nextTick1' yang sepadan dengan 'sync log', 'nextTick2', dan this.$refs.name.innerText >Apakah yang akan dikeluarkan masing-masing? Nota, apa yang dicetak di sini ialah InnerTeks DOM~ (jawapan akan disiarkan di penghujung artikel)

Jika anda mempunyai jawapan yang sangat tegas pada masa ini, maka anda tidak perlu teruskan membaca Okay~ Tetapi jika anda mempunyai kebimbangan tentang jawapan anda, maka anda juga boleh mengikuti saya dan membaca. Saya percaya selepas membacanya, anda akan mendapat jawapan yang pasti tanpa perlu melihat jawapannya~!


2. Pelaksanaan kod sumber NextTick

Kod sumber terletak dalam core/util/next-tick. Ia boleh dibahagikan kepada 4 bahagian. Pergi terus ke kod

1 Pembolehubah global

callbacksbaris gilir, pendingstatus

const callbacks = [] // 存放cb的队列
let pending = false // 是否马上遍历队列,执行cb的标志

2. flushCallbacks

Lintas panggilan balik dan laksanakan setiap 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 pelaksanaan tak segerak nextTick

mengguna pakai strategi pelaksanaan tak segerak yang berbeza mengikut tahap sokongan persekitaran pelaksanaan

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)
  }
}
    Berikut adalah kes sebenar untuk mendalami pemahaman
  • . Lagipun, berbanding dengan tiga penyelesaian tak segerak yang lain, penyelesaian ini sepatutnya menjadi yang paling tidak dikenali oleh semua orang MutationObserver
    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;)
  • Adakah anda tahu bagaimana output yang sepadan?
    • dan script start akan dilaksanakan dalam pusingan pertama tugasan makro, tiada masalah script end

    • akan diletakkan di dalam. pusingan seterusnya. Pusingan pelaksanaan tugas makro setTimeout

    • ialah tugasan mikro, jadi ia akan dilaksanakan selepas pusingan tugasan makro ini, jadi ia mendahului MutationObserversetTimeout

  • Hasilnya adalah seperti yang ditunjukkan di bawah:

  • Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue

4 nextTick,

Kaedah

cbPromise

Pergi ke butiran dan fahami kegunaan
export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  // 往全局的callbacks队列中添加cb
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, &#39;nextTick&#39;)
      }
    } else if (_resolve) {
      // 这里是支持Promise的写法
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    // 执行timerFunc,在下一个Tick中执行callbacks中的所有cb
    timerFunc()
  }
  // 对Promise的实现,这也是我们使用时可以写成nextTick.then的原因
  if (!cb && typeof Promise !== &#39;undefined&#39;) {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}
dan cara ia berfungsi?
  • pendingKes 1, dilaksanakan dua kali dalam pusingan Tick yang sama
  • ,
hanya akan dilaksanakan sekali

$nextTicktimerFunc

Lihat gambar Lebih intuitif?
this.$nextTick(() => console.log(&#39;nextTick1&#39;))
this.$nextTick(() => console.log(&#39;nextTick2&#39;))

Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue3. Kemas kini asynchronous komponen Vue


Jika terdapat sebarang komponenisasi Vue

,

Mengedarkan kemas kiniBagi mereka yang tidak tahu banyak tentangnya, anda boleh klik di sini untuk melihat ilustrasi grafik prinsip responsif VueKetahui tentang kemas kini komponen dan pengedaran Vue sebelum kembali~ Kemas kini tak segerak Vue

DOM sebenarnya dilaksanakan menggunakan

, yang sebenarnya sama $nextTick yang biasa kami gunakan~Di sini kami menyemak, apabila kami menukar atribut Apa yang berlaku apabila berbaloi ke? nextTick

Menurut rajah di atas, kita mulakan dari permulaan watcher.update, ambil Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vuerendering Watcher

sebagai contoh, dan masukkan

queueWatcher1

Apa yang anda lakukan?

queueWatcher

2
// 用来存放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)
    }
  }
}
Apa yang anda lakukan?

flushSchedulerQueue

Akhir sekali, gambar untuk memahami proses kemas kini tak segerak komponen
function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  flushing = true
  let watcher, id
  // 排序保证先父后子执行更新,保证userWatcher在渲染Watcher前
  queue.sort((a, b) => a.id - b.id)
  // 遍历所有的需要派发更新的Watcher执行更新
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    id = watcher.id
    has[id] = null
    // 真正执行派发更新,render -> update -> patch
    watcher.run()
  }
}

Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue 4. Berbalik kepada soalan itu sendiri


Saya percaya bahawa melalui analisis kod sumber nextTick di atas, kami telah mendedahkan tabir misterinya. Pada masa ini, anda mesti dapat memberitahu jawapan dengan tegas Tanpa berlengah lagi, mari kita semak bersama-sama dan lihat sama ada ia adalah apa yang anda fikirkan.

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

Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue

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

Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue

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

    • 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吗?

  • 最后来一张图加深理解

Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue


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

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

Atas ialah kandungan terperinci Berkongsi maklumat berguna untuk membantu anda memahami Vue.nextTick dalam Vue. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam