likes
comments
collection
share

Vue中$nextTick入门教程

作者站长头像
站长
· 阅读数 10

官方定义:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

理解:vue 在数据发生变化的时候并不会立即去更新 dom,而是开启一个异步更新队列,等待同一事件循环中所有的数据变化完成后,再进行 Dom 更新,所以更新 dom 的过程是异步执行的,此时我们想获取最新的 dom 就需要借助 $nextTick() 辅助函数。

举个🌰

<template>
    <div class="box">
      <div ref="dom">{{ msg }}</div>
      <el-button @click="update">点击更新</el-button>
    </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "更新前",
    };
  },
  methods: {
    update() {
      this.msg = "222";
      this.msg = "333";
      this.msg = "444";
      console.log(this.$refs.dom.innerHTML); // 初始值
    },
  },
};
</script>

分析:当我们触发 update 事件更新页面时候,我们发现 update 中获取不到更新后页面的值,这是由于 vue 在页面更新的时候采用的异步更新策略。 vue 会将同一个事件循环中所有需要修改的数据放在同一个异步队列中。(这里我们看到 msg 被修改了三次,实际在异步队列中只修改了一次。 )这是由于对于一直修改的数据,异步队列会进行去重处理。当同一事件循环中的数据更新完成后,才会将队列中的 update 事件进行处理,更新 Dom

为什么要使用异步更新策略?

{{num}}
for(let i=0; i<100000; i++){
    num = i
}

分析:比如上面这串代码,如果同步更新,每次循环都会更新页面,那么页面会更新 100000 次,这就造成了不必要的性能浪费。

$nextTick 的使用

<template>
    <div class="box">
      <div ref="dom">{{ msg }}</div>
      <el-button @click="update">点击更新</el-button>
    </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "更新前",
    };
  },
  methods: {
    update() {
      this.msg = "222";
      this.msg = "333";
      this.msg = "444";
      
      this.$nextTick(() => {
        console.log(this.$refs.dom.innerHTML); // 444
      });
      console.log(this.$nextTick()); // Promise {<pending>}
    },
  },
};
</script>

分析: 仍然是上面的 🌰,为什么在 $nextTick 的回调中可以获取到最新的 Dom ?首先我们在控制台可以看到 $nextTick 本质是一个 Promise 对象。所以$nextTick中执行的是一个异步的操作。

再举个🌰

<template>
  <div>
      <div ref="dom">{{ msg }}</div>
      <el-button @click="update">点击更新</el-button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "更新前",
    };
  },
  methods: {
    update() {
      this.msg = "222";
      this.msg = "333";
      this.msg = "444";
    
      setTimeout(() => {
        this.msg = "555";
      });

      Promise.resolve((this.msg = "666"));

      this.$nextTick(() => {
        console.log(this.$refs.dom.innerHTML); // 666
      });
    },
  },
};
</script>

分析: 在上面的栗子上如果加上 setTimeoutPromise 呢? 浏览器中的 msg 变成了 555 但是获取最新的 Dom 值仍然是 666 。这是由于 Promise.resolve 是微任务,而setTimeout 是宏任务,所以这里相当于重新开启了一个事件循环。如果不理解建议看下 javaScript浅谈----事件循环(event loop)

使用场景:

  1. created 中获取 dom
  2. 响应式数据更新完成后获取 dom 的更新状态,比如添加或者删除列表的中的一项,获取列表的高度。