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