Vue에서 Vue.nextTick을 이해하는 데 도움이 되는 유용한 정보를 공유합니다.

青灯夜游
풀어 주다: 2022-03-22 11:38:00
앞으로
2265명이 탐색했습니다.

이 글은 여러분과 함께 Vue당신이 모르는 Vue.nextTick의 순수한 정보와 소개가 모두에게 도움이 되길 바랍니다!

Vue에서 Vue.nextTick을 이해하는 데 도움이 되는 유용한 정보를 공유합니다.

Vue를 사용해본 친구들은 $nextTick을 어느 정도 알고 계시죠~ 정식으로 nextTick을 설명하기 전에, Vue가 업데이트할 때 비동기라는 점을 분명히 아셔야 할 것 같습니다. DOM.code>, 다음 설명 과정은 컴포넌트 업데이트와 함께 설명할 예정이니까요~ 더 이상 고민하지 않고 바로 본론으로 들어가겠습니다. (이 글에서는 v2.6.14$nextTick~ 在正式讲解nextTick之前,我想你应该清楚知道 Vue 在更新 DOM 时是异步执行的,因为接下来讲解过程会结合组件更新一起讲~ 事不宜迟,我们直进主题吧(本文以v2.6.14版本的Vue源码进行讲解)【相关推荐:vuejs视频教程

一、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>
로그인 후 복사

请问上述代码中,当点击按钮“修改name”时,&#39;nextTick1&#39;&#39;sync log&#39;&#39;nextTick2&#39;对应的this.$refs.name.innerText分别会输出什么?注意,这里打印的是DOM的innerText~(文章结尾处会贴出答案)

如果此时的你有非常坚定的答案,那你可以不用继续往下看了~但如果你对自己的答案有所顾虑,那不如跟着我,接着往下看。相信你看完,不需要看到答案都能有个肯定的答案了~!


二、nextTick源码实现

源码位于core/util/next-tick中。可以将其分为4个部分来看,直接上代码

1. 全局变量

callbacks队列、pending状态

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

2. flushCallbacks

遍历callbacks执行每个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的理解。毕竟比起其他三种异步方案,这个应该是大家最陌生的
    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;)
    로그인 후 복사
  • 知道对应的输出会是怎么样的吗?
    • script startscript end会在第一轮宏任务中执行,这点没问题

    • setTimeout会被放入下一轮宏任务执行

    • MutationObserver是微任务,所以会在本轮宏任务后执行,所以先于setTimeout

  • 结果如下图:
    Vue에서 Vue.nextTick을 이해하는 데 도움이 되는 유용한 정보를 공유합니다.

4. nextTick方法实现

cbPromise方式

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
    })
  }
}
로그인 후 복사
  • 深入细节,理解pending有什么用,如何运作?

案例1,同一轮Tick中执行2次$nextTicktimerFunc只会被执行一次

this.$nextTick(() => console.log(&#39;nextTick1&#39;))
this.$nextTick(() => console.log(&#39;nextTick2&#39;))
로그인 후 복사
  • 用图看看更直观?

Vue에서 Vue.nextTick을 이해하는 데 도움이 되는 유용한 정보를 공유합니다.


三、Vue组件的异步更新

这里如果有对Vue组件化派发更新不是十分了解的朋友,可以先戳这里,看图解Vue响应式原理了解下Vue组件化和派发更新的相关内容再回来看噢~

Vue的异步更新DOM其实也是使用nextTick来实现的,跟我们平时使用的$nextTick其实是同一个~

这里我们回顾一下,当我们改变一个属性值的时候会发生什么?

Vue에서 Vue.nextTick을 이해하는 데 도움이 되는 유용한 정보를 공유합니다.

根据上图派发更新过程,我们从watcher.update开时讲起,以渲染Watcher为例,进入到queueWatcher

1. queueWatcher做了什么?

// 用来存放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)
    }
  }
}
로그인 후 복사

2. flushSchedulerQueue 버전의 Vue 소스코드를 사용하여 설명합니다.) [ 관련 권장사항: vuejs 비디오 튜토리얼

< h2 data-id="heading-0">1. NextTick 퀴즈
  • nextTick을 정말 이해하셨나요? 어서 질문으로 직행~
  • 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()
      }
    }
    로그인 후 복사
위 코드에서

버튼을 클릭하면 "이름 수정", 'nextTick1', '동기화 로그'</ code>, <code>'nextTick2'의 해당 this.$refs.name.innerText 출력은 무엇입니까? 참고Vue에서 Vue.nextTick을 이해하는 데 도움이 되는 유용한 정보를 공유합니다. 여기에 인쇄된 내용은 DOM의 innerText입니다~(답변은 글 마지막에 게시됩니다)


이번에 매우 확고한 답변이 있다면 그럴 필요는 없습니다. 계속 읽어보세요~ 하지만 당신이 옳다면 당신의 답변에 대해 우려가 있다면 저를 팔로우하고 계속 읽어보세요. 꼭 읽어보시면 답을 보지 않고도 확실한 답을 얻으실 수 있을 거라 믿습니다~!

2. NextTick 소스코드 구현

소스코드는 core/util/next-tick에 있습니다. 4개 부분으로 나누어 코드🎜

1. 전역 변수🎜🎜🎜콜백 대기열, 보류 중으로 직접 이동할 수 있습니다. status 🎜
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))
로그인 후 복사
로그인 후 복사
🎜

2. flushCallbacks🎜🎜🎜콜백을 탐색하여 각 cb🎜rrreee🎜

3. nextTick의 비동기 구현🎜🎜🎜실행 환경의 지원 수준에 따라 다양한 비동기 구현 전략을 채택하세요🎜rrreee🎜🎜다음은 실제 사례입니다. MutationObserver에 대한 이해를 심화시킵니다. 결국, 다른 세 가지 비동기 솔루션에 비해 이 솔루션은 모든 사람에게 가장 생소할 것입니다. 해당 출력이 어떻게 될지 아시나요?

관련 라벨:
vue
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿