>웹 프론트엔드 >JS 튜토리얼 >Vue의 nextTick 메소드에 대한 자세한 소개

Vue의 nextTick 메소드에 대한 자세한 소개

亚连
亚连원래의
2018-06-08 17:40:192258검색

이 글은 주로 Vue의 nextTick 메소드에 대한 간단한 이해를 소개하며, 참고용으로 공유합니다.

Vue의 nextTick은 Vue의 DOM 비동기 업데이트와 관련되어 있으며 매우 흥미롭고 특별히 배웠습니다. nextTick의 소스 코드에는 많은 지식이 포함되어 있으며 그 중 많은 부분이 제가 잘 이해하지 못하는 부분도 있습니다. 저의 통찰력을 바탕으로 nextTick을 소개하겠습니다.

1. 예제

먼저 Vue의 DOM 업데이트와 nextTick의 역할에 대해 알아보기 위해 예제를 살펴보겠습니다.

Template

<p class="app">
 <p ref="msgp">{{msg}}</p>
 <p v-if="msg1">Message got outside $nextTick: {{msg1}}</p>
 <p v-if="msg2">Message got inside $nextTick: {{msg2}}</p>
 <p v-if="msg3">Message got outside $nextTick: {{msg3}}</p>
 <button @click="changeMsg">
  Change the Message
 </button>
</p>

Vue 인스턴스

new Vue({
 el: &#39;.app&#39;,
 data: {
  msg: &#39;Hello Vue.&#39;,
  msg1: &#39;&#39;,
  msg2: &#39;&#39;,
  msg3: &#39;&#39;
 },
 methods: {
  changeMsg() {
   this.msg = "Hello world."
   this.msg1 = this.$refs.msgp.innerHTML
   this.$nextTick(() => {
    this.msg2 = this.$refs.msgp.innerHTML
   })
   this.msg3 = this.$refs.msgp.innerHTML
  }
 }
})

클릭 전

클릭 후

그림을 보면 알 수 있습니다. msg1과 msg3에 표시된 내용은 아직 변환 전이고 표시된 내용은 msg2는 그 이후의 변환입니다. 근본적인 이유는 Vue의 DOM 업데이트가 비동기식이라는 것입니다(자세한 설명은 아래 참조).

2. 애플리케이션 시나리오

nextTick의 주요 애플리케이션 시나리오와 이유에 대해 알아 보겠습니다.

Vue 라이프사이클의 Created() 후크 함수에서 수행되는 DOM 작업은 Vue.nextTick()의 콜백 함수에 배치되어야 합니다.

DOM은 Created() 후크 함수가 실행될 때 실제로 전혀 렌더링되지 않으며, 이때 DOM 작업을 수행하는 것은 쓸데없는 일이므로 여기서는 DOM 작업을 위한 js 코드를 Vue.nextTick()의 콜백 함수에 넣어야 합니다. 이에 대응하는 것이 Mounted() Hook 함수인데, Hook 함수가 실행되면 모든 DOM Mounting과 Rendering이 완료되기 때문에 Hook 함수에서는 DOM 연산을 수행하는데 문제가 없을 것입니다.

데이터 변경 후 작업을 수행해야 하고, 이 작업에 데이터 변경에 따라 변경되는 DOM 구조를 사용해야 하는 경우 이 작업을 Vue.nextTick()의 콜백 함수에 넣어야 합니다.

구체적인 이유는 Vue의 공식 문서에 자세히 설명되어 있습니다.

Vue는 DOM 업데이트를 비동기식으로 수행합니다. 데이터 변경이 관찰될 때마다 Vue는 대기열을 열고 동일한 이벤트 루프에서 발생하는 모든 데이터 변경 사항을 버퍼링합니다. 동일한 감시자가 여러 번 트리거되면 대기열에 한 번만 푸시됩니다. 버퍼링 중 이러한 중복 제거는 불필요한 계산 및 DOM 작업을 방지하는 데 중요합니다. 그런 다음 다음 이벤트 루프 "틱"에서 Vue는 큐를 플러시하고 실제(중복 제거된) 작업을 수행합니다. Vue는 내부적으로 비동기 대기열에 기본 Promise.then 및 MessageChannel을 사용하려고 시도합니다. 실행 환경이 이를 지원하지 않으면 대신 setTimeout(fn, 0)이 사용됩니다.

예를 들어 vm.someData = 'new value'를 설정하면 구성 요소가 즉시 다시 렌더링되지 않습니다. 큐가 플러시되면 이벤트 루프 큐가 지워지는 다음 "틱"에서 구성 요소가 업데이트됩니다. 대부분의 경우 이 프로세스에 대해 걱정할 필요가 없지만 DOM 상태가 업데이트된 후에 뭔가를 하려는 경우 약간 까다로울 수 있습니다. Vue.js는 일반적으로 개발자가 "데이터 기반" 방식으로 생각하고 DOM을 직접 건드리지 않도록 권장하지만 실제로 그렇게 해야 할 때가 있습니다. 데이터 변경 후 Vue가 DOM 업데이트를 완료할 때까지 기다리려면 데이터 변경 직후 Vue.nextTick(콜백)을 사용할 수 있습니다. 이 콜백 함수는 DOM 업데이트가 완료된 후에 호출됩니다.

3. nextTick 소스 코드에 대한 간략한 분석

기능

Vue.nextTick은 2개의 매개변수(콜백 함수 및 콜백 함수 실행을 위한 컨텍스트)를 허용합니다. 콜백 함수가 제공되지 않으면 Promise 객체가 반환됩니다.

소스 코드

/**
 * Defer a task to execute it asynchronously.
 */
export 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]()
  }
 }

 // the nextTick behavior leverages the microtask queue, which can be accessed
 // via either native Promise.then or MutationObserver.
 // MutationObserver has wider support, however it is seriously bugged in
 // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
 // completely stops working after triggering a few times... so, if native
 // Promise is available, we will use it:
 /* istanbul ignore if */
 if (typeof Promise !== &#39;undefined&#39; && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError)
   // in problematic UIWebViews, Promise.then doesn&#39;t completely break, but
   // it can get stuck in a weird state where callbacks are pushed into the
   // microtask queue but the queue isn&#39;t being flushed, until the browser
   // needs to do some other work, e.g. handle a timer. Therefore we can
   // "force" the microtask queue to be flushed by adding an empty timer.
   if (isIOS) setTimeout(noop)
  }
 } else if (!isIE && typeof MutationObserver !== &#39;undefined&#39; && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === &#39;[object MutationObserverConstructor]&#39;
 )) {
  // use MutationObserver where native Promise is not available,
  // e.g. PhantomJS, iOS7, Android 4.4
  var counter = 1
  var observer = new MutationObserver(nextTickHandler)
  var textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
   characterData: true
  })
  timerFunc = () => {
   counter = (counter + 1) % 2
   textNode.data = String(counter)
  }
 } else {
  // fallback to setTimeout
  /* istanbul ignore next */
  timerFunc = () => {
   setTimeout(nextTickHandler, 0)
  }
 }

 return function queueNextTick (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
   timerFunc()
  }
  if (!cb && typeof Promise !== &#39;undefined&#39;) {
   return new Promise((resolve, reject) => {
    _resolve = resolve
   })
  }
 }
})()

먼저 nextTick에 정의된 세 가지 중요한 변수를 이해해 보겠습니다.

  1. callbacks: 실행해야 하는 모든 콜백 함수를 저장하는 데 사용됩니다.

  2. pending: 콜백 함수가 실행되고 있는지 표시하는 데 사용됩니다.

  3. timerFunc: 콜백 함수의 실행을 트리거하는 데 사용됩니다.

다음으로 nextTickHandler()함수에 대해 알아보세요.

function nextTickHandler () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
   copies[i]()
  }
 }

이 함수는 콜백에 저장된 모든 콜백 함수를 실행하는 데 사용됩니다.

다음 단계는 TriggerFunc에 트리거 메서드를 할당하는 것입니다.

먼저 Promise가 기본적으로 지원되는지 확인하세요. 그렇다면 Promise를 사용하여 콜백 함수 실행을 시작하세요.

그렇지 않으면 MutationObserver가 지원되면 관찰자 개체를 인스턴스화하고 변경 사항을 관찰할 때 모든 콜백 함수 실행을 실행하세요. 텍스트 노드.

둘 다 지원되지 않으면 setTimeout을 사용하여 지연을 0으로 설정하세요.

마지막으로 queueNextTick 함수입니다. nextTick은 즉각적인 함수이기 때문에 queueNextTick 함수는 사용자가 전달한 매개변수를 받아들이고 콜백에 콜백 함수를 저장하는 데 사용되는 반환 함수입니다.

위 그림은 전체 실행 과정을 보여주고 있는데, 핵심은 실행을 지연시키는 기능인 timeFunc()에 있습니다.

위의 소개에서 timeFunc()의 구현 방법에는 세 가지가 있음을 알 수 있습니다.

  1. Promise

  2. MutationObserver

  3. setTimeout

其中Promise和setTimeout很好理解,是一个异步任务,会在同步任务以及更新DOM的异步任务之后回调具体函数。

下面着重介绍一下MutationObserver。

MutationObserver是HTML5中的新API,是个用来监视DOM变动的接口。他能监听一个DOM对象上发生的子节点删除、属性修改、文本内容修改等等。

调用过程很简单,但是有点不太寻常:你需要先给他绑回调:

var mo = new MutationObserver(callback)

通过给MutationObserver的构造函数传入一个回调,能得到一个MutationObserver实例,这个回调就会在MutationObserver实例监听到变动时触发。

这个时候你只是给MutationObserver实例绑定好了回调,他具体监听哪个DOM、监听节点删除还是监听属性修改,还没有设置。而调用他的observer方法就可以完成这一步:

var domTarget = 你想要监听的dom节点
mo.observe(domTarget, {
   characterData: true //说明监听文本内容的修改。
})

在nextTick中 MutationObserver的作用就如上图所示。在监听到DOM更新后,调用回调函数。

其实使用 MutationObserver的原因就是 nextTick想要一个异步API,用来在当前的同步代码执行完毕后,执行我想执行的异步回调,包括Promise和 setTimeout都是基于这个原因。其中深入还涉及到microtask等内容,暂时不理解,就不深入介绍了。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

如何实现vue2.0响应式(详细教程)

详细讲解React中的refs(详细教程)

通过JS如何实现文字间歇循环滚动效果

위 내용은 Vue의 nextTick 메소드에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.