【问题标题】:What is nextTick and what does it do in Vue.js?什么是 nextTick 以及它在 Vue.js 中的作用?
【发布时间】:2018-05-17 23:18:44
【问题描述】:

看了the docs,还是看不懂。

我知道datacomputedwatchmethods 是做什么的,但是在 Vue.js 中 nextTick() 是做什么用的?

【问题讨论】:

  • 要理解的关键概念是 DOM 是 异步更新的。当您在 Vue 中更改值时,更改不会立即呈现到 DOM。相反,Vue 将 DOM 更新排队,然后在计时器上更新 DOM。通常,这发生得非常快,以至于没有什么区别,但是有时,您需要在 Vue 渲染后更新渲染的 DOM,您不能立即在方法中执行此操作,因为更新尚未发生然而。在这些情况下,您将使用nextTickDocumented here.
  • 补充上面@Bert 在https://stackoverflow.com/q/47634258/9979046 中所说的内容,nextTick() 将用于单元测试中,当您需要检查一个元素是否存在于 DOM (HTML) 中时,例如,如果您获取有关 Axios 请求的一些信息。
  • 为什么我觉得 nextTick 有点像 const nextTick = (callback, context) => { setTimeout(callback.bind(context), 0); };

标签: vue.js vuejs2


【解决方案1】:

一切都与时机有关

nextTick 允许您在更改一些数据之后执行代码,而 Vue.js 已根据您的数据更改更新了虚拟 DOM,但 浏览器之前已在页面上呈现该更改。

通常,devs use the native JavaScript function setTimeout 会实现类似的行为,但使用 setTimeout 会将控制权交给浏览器之前它会将控制权交还给您(通过调用您的回调)。

示例

假设您更改了一些数据; Vue 然后根据该数据更改更新 vDOM(浏览器尚未将更改呈现到屏幕上)。

如果此时您使用nextTick,您的回调将立即被调用,并且浏览器将在该回调完成执行后更新页面。

如果您改为使用setTimeout,则浏览器将有机会更新页面,然后您的回调将被调用。

您可以通过创建如下所示的小组件来可视化此行为:
(检查this fiddle 以查看它的实时状态)

<template>
  <div class="hello">
    {{ msg }}
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
        msg: 'One'
    }
  },
  mounted() {
      this.msg = 'Two';

      this.$nextTick(() => {
          this.msg = 'Three';
      });
  }
}
</script>

运行您的本地服务器。您将看到正在显示的消息“三”。

现在,将this.$nextTick 替换为setTimeout

setTimeout(() => {
    this.msg = 'Three';
}, 0);

重新加载浏览器。在看到“三”之前,您会看到“二”。

那是因为,setTimeout:

  1. Vue 将 vDOM 更新为“二”
  2. Vue 将控制权交给了浏览器
  3. 浏览器显示“二”
  4. 回调被调用
  5. Vue 将 vDOM 更新为“三”
  6. Vue 将控制权交给了浏览器
  7. 浏览器显示“三”

但是对于nextTick,我们跳过了第 2 步和第 3 步! Vue 不会在第一次 vDOM 更新后传递控制权,而是调用回调立即,这会阻止浏览器更新,直到回调完成。在本例中,这意味着“二”从未实际显示。

要了解 Vue 如何实现这一点,您需要了解 JavaScript Event Loopmicrotasks 的概念。

一旦您清楚(呃)这些概念,请查看source code for nextTick

【讨论】:

    【解决方案2】:

    Next Tick 基本上允许你在 vue 重新渲染组件之后运行一些代码,并且你对响应式属性(数据)进行了一些更改。

    // modify data
    vm.msg = 'Hello'
    // DOM not updated yet
    Vue.nextTick(function () {
       // this function is called when vue has re-rendered the component.
    })
        
    // usage as a promise (2.1.0+, see note below)
    Vue.nextTick()
       .then(function () {
           // this function is called when vue has re-rendered the component.
        })
    

    来自 Vue.js 文档:

    将回调推迟到下一个 DOM 更新周期后执行。更改一些数据后立即使用它以等待 DOM 更新。

    了解更多信息,here

    【讨论】:

    • 如何更新?这是我不明白的。如果我更新 vm.msg 则 dom 已经更新,因为有一个新文本 ''hello" .. 那么我怎样才能再次更新它呢?你能发布一个带有示例的小提琴吗?谢谢
    • 好的,我将编辑答案并尝试进一步解释。
    • @hidar 您可以在需要进行多次更新但又想在不同的 dom 周期中显式渲染彼此的情况下使用它
    • 这不是为了让你更新 DOM 本身,而是在它受到影响后对其进行任何操作(例如更新、从中读取信息等) /由 Vue 所做的更改修改(因为您更改了反应属性值等)。
    • 这是一个让它更简单的例子。
    【解决方案3】:

    为了让 Pranshat 回答使用 nextTicksetTimeout 之间的区别,更明确地说,我分叉了他的小提琴: here

    mounted() {    
      this.one = "One";
         
      setTimeout(() => {
        this.two = "Two"
      }, 0);
          
      //this.$nextTick(()=>{
      //  this.two = "Two"
      //})}
    }
    

    您可以在小提琴中看到,当使用setTimeOut 时,一旦组件在适应更改之前被挂载,初始数据会非常短暂地闪烁。然而,当使用nextTick 时,数据会在呈现给浏览器之前被劫持、更改。因此,浏览器显示更新的数据,甚至不知道旧数据。 希望一举搞定这两个概念。

    【讨论】:

      【解决方案4】:

      我已经创建了一个有用的演示,在什么情况下我们可以在 Vuejs 中使用 nextTick,如果您想在 DOM 更新后立即更新或运行某些内容,请参阅 addMessage 函数,我正在调用另一个函数,其中我正在使用 nextTick 函数更新滚动条以查看最新消息。

      <!DOCTYPE html>
      <html>
          <head>
              <title>CDN VUE 3</title>
          </head>
          <body>
              <div id="app">
              <div ref="scrolledList" style="height: 100px; width: 150px; border:1px solid red; overflow: auto; margin-bottom: 15px; padding: 5px;">
                 <ul ref="scrolledHeight" style="margin: 0; padding: 0;">
                     <li v-for="msg in messages">
                         {{msg}}
                     </li>               
                 </ul>
              </div>
                 <input type="text" placeholder="Add Message" v-model="message" />
                 <button @click="addMessage" @keyup.enter="addMessage"> Add Message</button>
              </div>
              <script src="https://unpkg.com/vue@next"></script>
              <script>
                  Vue.createApp({
                      data() {
                          return {
                             message: '',
                             messages: [1,2,3,4,5,6,7,8,9,10]
                          }
                      },
                      mounted() {
                          this.updateScrollNextTick()
                      },
                      methods: {
                          addMessage() {
                              if(this.message == ''){
                                  return
                              }
                              this.messages.push(this.message)
                              this.message = ""
                              this.updateScrollNextTick()
                          },
                          updateScrollNextTick () {
                              this.$nextTick( () => {
                                  let scrolledHeight = this.$refs.scrolledHeight.clientHeight
                                  this.$refs.scrolledList.scrollTo({
                                      behavior: 'smooth',
                                      top: scrolledHeight
                                  })
                              })
                          }
                      },
                  })
                  .mount("#app")
              </script>
          </body>
      </html>

      【讨论】:

        猜你喜欢
        • 2023-03-15
        • 2020-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多