5.8.2 建立响应联系
Set / Map 的读写都要通过自定义方法完成:读
size追ITERATE_KEY,add/delete里手动trigger。
一、读 size:与 ITERATE_KEY 建立联系
get(target, key, receiver) {
if (key === 'raw') return target
// 读取 size 属性
if (key === 'size') {
track(target, ITERATE_KEY) // 新增:建立响应联系
return Reflect.get(target, key, target) // receiver 用原始对象,见 5.8.1
}
// ...
}
之所以追
ITERATE_KEY而不是'size':任何新增、删除操作都会影响size,而ADD/DELETE类型的trigger会连带触发ITERATE_KEY的依赖(见 5.3)。
二、自定义 add / delete
把集合方法定义在 mutableInstrumentations 中,并在 get 拦截中优先返回:
const mutableInstrumentations = {
add(key) {
// this 是代理对象,用 raw 拿到原始数据对象
const target = this.raw
const hadKey = target.has(key) // 先判断值是否已存在
const res = target.add(key) // 通过原始对象调用,无需再 bind
if (!hadKey) {
trigger(target, key, 'ADD') // 只有新增才触发响应
}
return res
},
delete(key) {
const target = this.raw
const hadKey = target.has(key)
const res = target.delete(key)
if (hadKey) {
trigger(target, key, 'DELETE') // 只有确实删除了才触发响应
}
return res
}
}
// get 拦截函数内:
if (mutableInstrumentations.hasOwnProperty(key)) {
return mutableInstrumentations[key]
}
要点:
- 通过
this.raw拿到原始数据对象后再调用原生方法,不再需要.bind; add与delete的触发条件恰好相反:add只在元素不存在时触发,delete只在元素确实存在时触发,避免无意义的更新。
const p = reactive(new Set([1, 2, 3]))
effect(() => {
console.log(p.size) // 1→ 4 → 3
})
p.add(5) // 触发
setTimeout(() => p.delete(5), 1000) // 触发
相关
- 5.8.1 如何代理Set和Map:
size与原型方法的报错原因 - 5.8.3 避免污染原始数据:Map 的
get/set以及原始数据被污染的问题