美文网首页
vue异步更新流程梳理

vue异步更新流程梳理

作者: 0月 | 来源:发表于2021-07-09 21:21 被阅读0次

前言

vue实例创建后,当我们重新赋值data中的数据时,视图就会更新,那么具体干了啥呢?本文用demo + 调试断点,一步步来研究一下具体流程。

demo代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">{{value}}</div>
  <script src="../dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data() {
        return {
          value: 1
        }
      },
      mounted () {
        this.value = 2
      }
    })
  </script>
</body>
</html>

众所周知,vue在实例化的时候会对data的数据进行响应式设置,当我们赋值的时候就会触发对应的setter,所以把断点打到setter看一下,如下图:


image.png

我们传入的newVal 是 2,老的val 是1,后面直接val = newVal ,那么此时val就更新为2了。更新之后,最后还有一句代码

dep.notify();

这里就是对观察者们进行通知了。当我们的值变化时,dep就负责去通知watcher们,每一个watcher实例就会调用自己内部的update方法。咱门进入notify方法看看它是不是这样子:


image.png

看果然是这样子,subs这个数组就是专门存放watcher实例的,循环遍历watcher,调用每个watcher自己的update方法。
这里只存放了一个watcher实例,看看这个watcher是啥。这里先解释一下watcher都有哪些分类:

  1. 渲染watcher, 负责更新视图变化的,即一个vue实例对应一个渲染watcher
  2. 用户自定义watcher,用户通过watch:{value(val, oldVal){}}选项定义的,或者this.$watch()方法生成的。
  3. computed选项里面的计算属性也是watcher, 和第2点中的watcher的区别是它的watcher实例有dirty属性控制着watcher.value值的变化

打开右边的scope栏,找到Local下的subs[0]


image.png

再去查看watcher.vm._wather是否有值,有就代表是渲染watcher。同时找到源码Watcher的声明:


image.png

由此可见我们现在正在执行update的这个watcher就是一个渲染watcher。
继续找看update方法干了啥:

image.png
update就是执行queueWatcher(this),再看这里干了啥
image.png
其实猜也能猜到就维护一个queue:[watcher, ...]的队列,里面的watcher不重复;最后执行nextTick(flushSchedulerQueue); 看看flushSchedulerQueue干了啥,其实就是执行watcher.run方法。
image.png

来到这里我们大概就清晰了这个流程:
赋值操作this.value = 2就是把渲染watcher放到一个queue的队列中,并且通过nextTick在将来某个时刻把queue队列的watcher拿出来一个个去执行watcher.run()方法。

这里的nextTick就是利用事件循环,在未来某个时刻会执行flushSchedulerQueue方法 , flushSchedulerQueue 又是循环执行 watcher.run();继续回到watcher看run方法干了啥:


image.png
image.png

注意观察上面两张图, 在run方法中执行this.get(),视图就改变了值,从1变成2,一切的谜团就在这一行代码中,我们继续研究get()方法!

image.png

get里面调用了this.getter.call(vm, vm),找到this.getter,发现这是一个updateComponent的方法:


image.png

那么这个方法是怎么来的呢?


image.png

如上图,是watcher实例化的第二个参数: expOrFn,那么就打断点在watcher实例化过程中,看看这个updateComponent怎么来的。刷新页面:


image.png
根据上图中1、2步骤,在call stack执行栈中往下点,一个个方法进去看,很幸运,在下面的mountComponent方法的执行栈就看到了updateComponent的声明,在下图2处,就是updateComponent这个方法做的事情,从方法中我们可以知道,就是这行代码起了作用
 vm._update(vm._render(), hydrating);
image.png

总结

那么,至此我们再总结一下流程:
this.value = 2 触发 setter,
同步执行过程:
setter => dep.notify() => watcher.update() =>queueWatcher(this) =>nextTick(flushSchedulerQueue);

由于把flushSchedulerQueue放到了nextTick里面,那么接下来未来的某个时刻会执行flushSchedulerQueue,然后从queque队列中提取watcher出来循环执行watcher.run

watcher.run() => watcher.get() => watcher.getter() => updateComponent() => vm._update(vm._render())

到此为止我们就知道大概就是这么一个流程。
最后再看看_update()方法与_render()方法,
_render()方法很纯粹,就是返回一个虚拟dom: vnode对象。而_update()方法就是把虚拟dom: vnode去进行patch的过程,得到一个新的真实dom。

一些问题:

  • 为什么watcher放在queue队列中不直接去执行watcher.run呢,而要放到nextTick里面,等待未来某个时刻统一执行呢?
    答:其实还是为了性能,高效,如果你这么写代码:
this.value = 2
this.value = 3

没有nextTick就会走两次 vm._update(vm._render())了,而这里面的patch过程的diff就是一个比较复杂消耗性能的过程。

  • 为什么只有一个渲染watcher?
    答: 因为vue1.x就是因为采用了一个绑定值一个watcher的方式,虽然变化可以精确到绑定值的位置,但是这样子在大一点的项目就很多watcher,会消耗大量内存造成性能瓶颈,vue2采用了虚拟dom更新的方案,以组件为单位进行更新,一个组件实例对应一个渲染watcher。组件内的一个或者多个响应式属性更新 --> 触发渲染watcher.unpdate() --> 同一个渲染watcher只被送入一次queueWatcher队列 -->nextTick之后的回调里面触发watcher.run()。

相关文章

  • 彻底理解vue里面的各种watcher及其作用

    vue中的watcher的分类 在文章vue异步更新流程梳理[https://www.jianshu.com/p/...

  • vue异步更新流程梳理

    前言 vue实例创建后,当我们重新赋值data中的数据时,视图就会更新,那么具体干了啥呢?本文用demo + 调试...

  • 彻底理解vue的patch流程和diff算法

    前言 上一篇《vue异步更新流程梳理》[https://www.jianshu.com/p/bd6f66a1b15...

  • Vue.$nextTick用法

    vue是数据驱动视图更新,但vue数据变化后,视图不会立即更新,而是异步的过程.具体的更新时机参考主队列,异步队列...

  • 05Vue源码剖析2

    Vue 源码剖析2 异步更新队列 Vue 高效的秘诀是一套批量、异步的更新策略 概念解释 事件循环 Event L...

  • 解析Vue.nextTick

    一、原理 1、vue异步异步更新队列(JS运行机制 、 事件循环)Vue 在观察到数据变化时并不是直接更新 DOM...

  • vue使用this.$nextTick()函数

    Vue.nextTick Vue 中的 nextTick 涉及到 Vue 中 DOM 的异步更新 Vue 实现响应...

  • Vue 异步更新及nextTick原理

    目标 理解 Vue 异步更新机制 理解 nextTick 原理   先来看看官网关于 异步更新队列[https:/...

  • Vue异步更新

    当调用set方法时会走dep.notify()然后watcher.update()->queueWatcher()...

  • 为什么Vue采用异步渲染呢?

    Vue 是组件级更新,如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能, Vue 会在...

网友评论

      本文标题:vue异步更新流程梳理

      本文链接:https://www.haomeiwen.com/subject/smmtpltx.html