vm.$nextTick

前言:

深入浅出Vue的部分个人理解。

nextTick接收一个回调函数作为参数,它的作用是将回调函数延迟到下次DOM更新周期之后执行。它与全局方法Vue.nextTick一样,不同的是回调的this自动绑定到调用它的实例上。如果没有提供回调且在支持Promise的环境中,则返回一个Promise。

我们在开发项目时会遇到一种场景,当更新了状态(数据后),需要对新DOM做一些操作,但是这时我们其实获取不到更新后的DOM,因为还没有重新渲染,这个时候我们需要使用nextTick方法。

有一个问题:下次DOM更新周期之后执行,具体指什么时候呢? 要搞清楚这个问题,就要先弄明白什么是“下次DOM更新周期”。

    在Vue.js中,当状态发生变化时.watcher会得到通知,然后出发虚拟DOM的渲染流程。而watcher触发渲染这个操作并不是同步的,而是异步的。Vue.js中有一个队列,每当需要渲染时,会将watcher推送到这个队列中,在下一次事件循环中再让watcher触发渲染的流程。

1.为什么vue.js使用异步更新队列

我们知道Vue.js2.0开始使用虚拟DOM进行渲染,变化侦测的通知只发送到组件,组件内用到的所有状态的变化都会通知到同一个watcher,然后虚拟DOM会对这个组件进行比对(“diff”)并更改DOM

也就是说,如果在同一轮事件循环中两个数据发送了变化,那么组件的watcher会收到两份通知,从而进行两次渲染。事实上,并不需要渲染两次,虚拟DOM会对整个组件进行渲染,所以只需要等所有状态都修改完毕后,一次性将整个组件的DOM渲染到最新即可。

2.什么是事件循环

我们都知道Javascript是一门单线程且非阻塞的脚本语言,这意味着JavaScript代码在执行的任何时候只有一个主线程来处理所有任务。

异步任务有两种类型:微任务和宏任务。不同类型的任务会被分配到不同的任务队列中去。

当执行栈中的所有任务都执行完毕后,会去检查微任务队列中是否有事件存在,如果存在,则会依次执行微任务队列中事件对应的回调,直到为空。然后去宏任务队列中取出一个事件,把对应的回调加入到当前执行栈,当执行栈中的所有任务都执行完毕后,检查微任务队列中是否有事件存在。无限重复此过程,就形成了一个无限循环,这个循环就叫事件循环。

属于微任务的事件

Promise.then

Process.nextTick

属于宏任务的事件包含

setTimeout

setInterval

3.$nextTick更新是异步的

(1)当更新了状态(数据后),需要对更新后的DOM进行一些操作,但是获取不到更新后的DOM。使用$nextTick这个api可以解决这个问题。

代码:

<template>
  <div class="">
    <h1 ref="aa">{
   
   { number }}</h1>
    <button @click="fn">点击修改</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      number: 1,
    };
  },
  name: "",
  methods: {
    fn() {
      this.number++;
      console.log(this.$refs.aa.innerHTML);
    },
  },
};
</script>

<style scoped></style>

view:

可以观察到的是页面上显示的是2,实际获取的是1。

在使用了$nextTick之后。

代码:

<template>
  <div class="">
    <h1 ref="aa">{
   
   { number }}</h1>
    <button @click="fn">点击修改</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      number: 1,
    };
  },

  name: "",

  methods: {
    fn() {
      this.number++;
      this.$nextTick(() => {
        console.log(this.$refs.aa);
      });
    },
  },
};
</script>

<style scoped></style>

view:

实现了同步。

4.Vue渲染是批量进行渲染的

代码:

<template>
  <div class="">
    <ul ref="aa">
      <li v-for="(itme, index) in arr" :key="index">{
   
   { itme }}</li>
    </ul>
    <button @click="fn">点击</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: [10, 20, 30, 40],
    };
  },
  name: "",
  methods: {
    fn() {
      this.arr.push(Math.random());
      this.arr.push(Math.random());
      this.arr.push(Math.random());
      this.$nextTick(() => {
        console.log(this.$refs.aa.childNodes.length);
      });
    },
  },
};
</script>

<style scoped></style>

点击按钮push进去三个随机数,并且获取到更新后的真实的长度。

view:

可以看出直接打印出渲染玩之后的长度。证明是批量渲染的。

论证了这句话:

也就是说,如果在同一轮事件循环中两个数据发送了变化,那么组件的watcher会收到两份通知,从而进行两次渲染。事实上,并不需要渲染两次,虚拟DOM会对整个组件进行渲染,所以只需要等所有状态都修改完毕后,一次性将整个组件的DOM渲染到最新即可。

5.总结

1.应用:如果更新的状态之后想要对更新后的DOM进行操作可以使用$nextTick这个api。此时的整个组件异步更新完成。

2.Vue组件的更新是异步的,是一个微任务。通过diff算法在进行比对完毕后,一次性进行渲染,而不是更新了一次状态就去通知watcher进行改变。

猜你喜欢

转载自blog.csdn.net/qq_59076775/article/details/124576667
vm