One minute
「Vue3」reactivity 模块
模块概述
- 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)
})
可以看到,经过了如下步骤:
- 利用 reactive 将 { num: 0 } 封装为响应式对象并赋值给 counter
- 利用 effect 将 () => (dummy = counter.num) 封装为副作用
- effect 内部 fn 会立即执行,即触发 counter 的 get,并给 dummy 赋值
- get 触发,开始采集依赖(track),将此时的 activeEffect 作为依赖收集
- 对 dummy 进行断言,值为 0
- 给响应式对象 counter 的 num 属性赋值,触发 counter 的 set
- set 触发,开始触发依赖(trigger),将 counter 对应的所有依赖依次触发
- effect 内部 fn 再次执行,给 dummy 赋值
- 对 dummy 进行断言,值为 7
总结
3.0 的 reactivity 模块对比 2.0 的响应式系统 * 加入了 Proxy 来更加完善的代理对象中的方法(例如 2.0 中较为诟病的数组方法)以替换原来的 Object.definePropert ,但同时也放弃了对低版本浏览器的兼容。 * 加入了 effect 副作用来更好的描述依赖的收集、触发 * 为所有 reactive 封装的对象维护了各自的映射表,避免重复封装、循环调用,由此也提供了撤销响应式的方法(toRaw) * 抽离出作为单独模块加载,结构更清晰,更易理解,同时支持了 hooks