Vue之异步更新队列

486 阅读2分钟

为什么我的数据明明发生变化了却没有在watch内被监听到?

如果你也遇到了这个问题,就赶紧往下看吧。

场景如下:我将初始化数据 0 传入子组件后通过事件监听的方式拿到了子组件返回的数据,返回的数据由父组件处理并保存到数据库后将被重新还原为 0 。在此过程中,通过 watch 对数据的变化进行监听,期望是在数据发生变化的情况下向用户或者开发者自己做出数据已发生改变的提醒。

但事实上会监听到吗?我将代码过程简略了一下,通过在按钮的事件处理方法中模拟数据的改变过程,并在watch中监听数据的更新。你可以尝试一下,控制台中是否有内容打印。

点击这里使用在线代码(可能需要科学上网)

可以复制代码进行本地测试

<template>
  <div id="app">
      n:{{ n }}
      <button @click="addAndSub">+1后立即减1</button>
  </div>
</template>

<script>

export default {
  name: "App",
  data() {
    return {
      n:1
    }
  },
  methods: {
    addAndSub: function() {
      this.n = 2
      this.n = 1
      //this.$nextTick(() => {
      //    this.n = 1
      //}
    }
  },
  watch: {
    n() {
      console.log('n has changed!');
    }
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
  border: 1px solid red;
}
</style>

代码的执行结果:

  • 如果不用nextTick 那么控制台不会有内容输出。这表示Vue并没有获取到数据的改变;
  • 用了nextTick 那么控制台有内容输出,且一次点击输出两次n has changed;

Vue的DOM更新是异步的!,DOM的更新不和代码的执行同步,也就是说,在DOM更新前,它是不会管你对数据更新几次的,它只会在要更新DOM时,读取到最新的数据去完成更新操作就行了。

而在我们的这个例子中,n 的变化是从 1 --> 2 --> 1 ,等到Vue要打算执行DOM更新时,通过DOM diff 发现你这数据是 1 ,开始是1,现在还是1,没有改变,那么就没必要执行更新DOM操作了,于是乎就发生了本文开始的那个问题“为什么数据发生改变了却没被监听到?”

由此我们可以知道,Vue中的watch属性,不是数据有变化就被触发,而是数据通过DOM diff明确了数据确实发生变化,那么才会被触发!

为什么使用了nextTick数据就会被监听到了呢?

因为nextTick的作用是‘隔离’或者叫‘同步’DOM的更新过程,在上一次的DOM更新完成后,才会继续完成nextTick回调内产生的新的DOM更新。那么上述代码中数据的变化过程就变成:

  1. 先从 1 --> 2,执行完毕,数据发生变化,触发watch;
  2. 再次从 2 --> 1,执行完毕,数据发生变化,触发watch;

这样的步骤就如同一个队列一般,按照划分好的步骤,依次完成。

什么是异步更新队列,你明白了吗?了解nextTick具体怎么用,看官方文档吧!