数据监听

Vue 3中数据监听的实现

众所周知,Vue 2采用的是Object.defineProperty()语法来完成针对对象元素的观察监听;而Vue 3则采用了别具一格的Proxy代理模式来完成对任何复杂数据类型的代理监听。通过对Vue两个版本实现数据监听的api特性进行对比,我们可以发现:

  • Object.defineProperty由于每次只能监听对象一个键的get、set,导致需要循环监听,浪费性能,而vue3Proxy可以一次性监听到所有属性
  • Object.defineProperty无法监听数组,必须通过增强并替换原型链方法的方式处理数组监听,而Proxy则可以直接监听数组变化
  • 由于Object.defineProperty只能监听对象,导致vue2data属性必须通过一个返回对象的函数方式初始化,而vue3则更加多元化,可以监听任意数据

在日常开发中,vue3提供的响应式api,最常用的便是reactive、ref、computed三种。

响应式 API

  • reactive依托于Proxy语法,分别在访问器特性中的get中触发trackset中触发trigger实现数据获取时的依赖收集和数据变化时的触发更新
  • ref则依托于RefImpl实现类中维护value属性的gettersetter,使实例在使用value属性时分别触发tracktrigger方法,实现依赖收集和触发更新
  • computed方法依托于ComputedImpl实现类,完成对getter函数中的数据进行依赖收集,最后通过构造器中effect属性,构建一个包含调度器的副作用函数来实现数据更新

reactive.ts

reactive.ts为我们提供了reactive、shallowReactive、readonly、shallowReadOnly、isProxy、toRaw、markRawvue3框架导出api。同时维护了诸如SKIP、IS_REACTIVE、IS_READONLY、RAW这些用于标识的私有属性:

export interface Target {
  [ReactiveFlags.SKIP]?: boolean // 是否无效标识,用于跳过监听
  [ReactiveFlags.IS_REACTIVE]?: boolean // 是否已被reactive相关api处理过
  [ReactiveFlags.IS_READONLY]?: boolean // 是否被readonly相关api处理过
  [ReactiveFlags.RAW]?: any // 当前代理对象的源对象,即target
}

以及四个基于WeakMap数据类型的代理缓存弱键仓库,用于提升代码运行时的内存性能:

export const reactiveMap = new WeakMap<Target, any>()
export const shallowReactiveMap = new WeakMap<Target, any>()
export const readonlyMap = new WeakMap<Target, any>()
export const shallowReadonlyMap = new WeakMap<Target, any>()

Links