Vue 的响应式原理概述

1. 侦测数据的变化(数据劫持 / 数据代理)
– Vue2.0——Object.defineProperty()
- 无法检测到对象属性的添加或删除
- 无法监听数组的变化,需要进行数组方法的重写
– Vue3.0——Proxy
- 直接代理整个对象,不需要遍历对象的每个属性
- 支持代理数组的变化
2. 收集视图依赖了哪些数据(依赖收集)

  • 在getter中收集依赖,在setter中触发依赖,
  • 将观察者Watcher对象存放到当前闭包中的订阅者Dep的subs中。

3. 数据变化时,自动“通知”需要更新的视图部分,并进行更新(发布订阅模式)
         当对象的值变化的时候,会触发对应的setter,setter通知之前依赖收集得到的Dep中的每一个watcher,告诉他们自己的值改变了,需要重新渲染视图,这时候Watcher就会开始调用update来更新视图。

在 Vue 中会把传入的 data 转换成 Observer 对象,Observer 接收的对象可能是数组,或纯对象。

  1. 纯对象情况
    1. 如果是纯对象, 则遍历这个对象,通过 Object.defineProperty 拦截对成员的遍历
    2. 并且为每一个成员创建一个 Dep, 每个成员在被读取的时候会收集当前的 watcher
    3. 【注释】watcher 分为三种
      1. render watcher, Dep 收集到这个 watcher 后,调用 notify 会更新当前组件
      2. computed watcher, vm 在读取 computed 时从计算当前的 computed, 并且里面有访问 vm 的 data 成员的话,会被 data 成员对应的 Dep 收集,当成员修改后会重新调用 computed 计算新的值
      3. user watcher, 为每一个 user watcher 定义一个 watcher, getter 则是组成一个读取成员的方法, 当读取 vm 中的成员之后会收集到 user watcer, 当成员修改后,会重新执行 user watcher 的回调函数
    4. watcher 中记录了处理函数, 比如 render watcher 的处理过程就是调用 vm 的 render 生成 vnode, 再调用 update 的 __patch__ 对新旧 vnode 进行 diff 或直接挂载
    5. 当成员修改之后,会通知所有收集到的 watcher, 调用对应的 update 来重新执行处理函数 getter
  2. 数组情况
    1. 数组情况在 data 的二级成员以后,当 observer 遇到数组成员后,会将数组成员中带副作用的方法进行重写, 并且将所有子元素都转换成 observer 对象
    2. 当调用重写的方法后,如果有新增的成员,会将新增的成员也转换 observer, 并且调用对应 Dep 的 notify 通知更新
    3. 数组的 Dep 是在成员访问的时候进行 watcher 收集的,data 成员通过递归下级成员,获取到 childObj, childObj 其中就可能是数组 Dep
  3. 如果非数组或非纯对象,则会跳过不进行 observer 转换
    Vue 的响应式原理概述