001 vue响应式的异步渲染

问题场景

    // DOM节点
    <template>
      <div>
        <div v-if="isShow" ref="show">{{data1}}</div>
        <div v-else ref="hidden">{{data2}}</div>
      </div>
    </template>

    <script>
      this.ishow = false
      console.log(this.$refs.hidden.offsetHeight)
    </script>

  此时,会打印出 undefined,很奇怪呀

 

分析

  这是因为vue是数据驱动视图的,即数据更新之后,vue会自动去更新视图,但是这个更新是异步的。即当数据更新之后,试图还未更新,此时继续操作更新后的dom,就会出现和预期不一样的结果。

 

什么叫异步呢

  当执行一段js代码时,引起了页面的数据变化,而这些数据变化会引起dom树的更新,但这个dom树的更新并不是立即执行的。此时,vue会将更新dom数的这一任务放到 异步更新队列 中去排队,在下一轮事件循环中执行。

 

为什么vue采用异步更新策略呢

  一句话:为了提升vue的性能,减少资源消耗

  我们可能在uige方法中对dom操作n多次(比如for循环对dom进行操作),如果每次都立即对dom进行更新,那么dom会频繁更新,这显然效率低下,异步更新,可以实现多次操作dom,一次更新视图。

 

nextTick()的使用:

  1. nextTick方法时解决上述异步问题的一个思路。this.$nextTick(callback)

  2. nextTick的核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout的原生 JS 方法来模拟对应的微/宏任务的实现;

  3. 它将传入的回调函数放在微/宏任务队列中等待执行,我们将更新数据之后,对更新后dom的操作放在$nextTick中去执行,就可以保证在dom更新完成之后,实现对dom的操作(因为这一步在队列中排在dom更新之后)

 

nextTick()的使用原理:

  nextTick内部对异步更新队列,按顺序采用Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替,进行降级处理。

    降级的目的:就是在环境支持的情况下,尽早的去更新dom。

    优先选择微任务的原因:在微任务中更新队列是会比在宏任务中更新少一次UI渲染的。

 

nextTick的更新

  过程

    1. Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 中 观察到数据变化的 watcher 推送进这个队列。

    2. 如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据,避免不必要的计算和Dom操作。

    3. 而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。

 

  源码解析

    1. 数据变化时,通过notify通知watcher进行更新操作;

    2. 通过subs[i].update依次调用watcher的update(未更新视图);

    3. 将watcher放到队列中,在queueWatcher会根据watcher的id进行去重(多个属性依赖一个watcher),如果队列中没有该watcher就会将该watcher添加到队列中(未更新视图);

    4. 通过nextTick异步执行flushSchedulerQueue方法刷新watcher队列(更新视图)

 

posted @ 2022-06-09 17:17  CarreyB  阅读(677)  评论(0编辑  收藏  举报