模块概述

  • baseHandlers.ts
    • 响应式对象 Proxy 的各个处理方式
    • 追踪数组变化 (includes、indexOf、lastIndexOf)
    • 对比 2.0,新增 Proxy、Reflect
  • collectionHandlers.ts
    • 采集处理 (Set、Map、WeakMap、WeakSet)
    • 构建迭代器
    • 检测内部变化
  • computed.ts
    • 是否只读通过传入参数是 object 还是 function 判断
    • 获取 value 时收集依赖
  • effect.ts
    • 封装副作用函数
    • 单独维护一个副作用堆(effectStack),防止循环调用
    • track 采集 [副作用] 添加到 [依赖] 中
    • trigger 触发 [依赖] 中的 [副作用]
  • operations.ts
    • 常量定义:采集类型、触发类型
  • reactive.ts
    • reactive:将对象转为响应式对象
    • shadowReactive:返回对象的响应式副本
    • readonly:将对象转为只读对象
    • shadowReadonly:返回对象的只读副本
    • 以上四种各自维护一个字典映射转换前后对象,防止重复转换
    • 利用对应的 Proxy 配置将对象转换为 observed 观察者对象
    • isReactive 判断当前对象是否是响应式对象
    • isReadonly 判断当前对象是否是只读对象
    • isProxy 传入对象是否经过代理(响应式对象 或 只读对象)
    • toRaw 将响应式对象、只读对象转为原始值
    • markRaw 将传入对象标记为原生对象,禁止转换
  • refs.ts
    • 类似computed.ts,对ref传入的值、对象进行封装。

reactive + effect 使用

结合vue官方提供的测试用例:

it('should observe basic properties', () => {
    let dummy
    const counter = reactive({ num: 0 })
    effect(() => (dummy = counter.num))
    expect(dummy).toBe(0)
    counter.num = 7
    expect(dummy).toBe(7)
})

可以看到,经过了如下步骤:

  1. 利用 reactive 将 { num: 0 } 封装为响应式对象并赋值给 counter
  2. 利用 effect 将 () => (dummy = counter.num) 封装为副作用
  3. effect 内部 fn 会立即执行,即触发 counter 的 get,并给 dummy 赋值
  4. get 触发,开始采集依赖(track),将此时的 activeEffect 作为依赖收集
  5. 对 dummy 进行断言,值为 0
  6. 给响应式对象 counter 的 num 属性赋值,触发 counter 的 set
  7. set 触发,开始触发依赖(trigger),将 counter 对应的所有依赖依次触发
  8. effect 内部 fn 再次执行,给 dummy 赋值
  9. 对 dummy 进行断言,值为 7

总结

3.0 的 reactivity 模块对比 2.0 的响应式系统 * 加入了 Proxy 来更加完善的代理对象中的方法(例如 2.0 中较为诟病的数组方法)以替换原来的 Object.definePropert ,但同时也放弃了对低版本浏览器的兼容。 * 加入了 effect 副作用来更好的描述依赖的收集、触发 * 为所有 reactive 封装的对象维护了各自的映射表,避免重复封装、循环调用,由此也提供了撤销响应式的方法(toRaw) * 抽离出作为单独模块加载,结构更清晰,更易理解,同时支持了 hooks