5.8.6 values与keys方法
values与keys的实现几乎一样,但追踪的 key 不同,避免值变化时触发只关心键的迭代。
一、values:只包装值
values 与 entries 的实现非常相似,只有两点区别:
| 对比项 | iterationMethod(entries / Symbol.iterator) | valuesIterationMethod(values) |
|---|---|---|
| 取原始迭代器 | target[Symbol.iterator]() | target.values() |
| 处理的值 | 键值对 [wrap(value[0]), wrap(value[1])] | 仅值 wrap(value) |
function valuesIterationMethod() {
const target = this.raw
const itr = target.values() // 通过 values 获取原始迭代器
const wrap = (val) => (typeof val === 'object' ? reactive(val) : val)
track(target, ITERATE_KEY) // values 关心键也关心值
return {
next() {
const { value, done } = itr.next()
return { value: wrap(value), done }
},
[Symbol.iterator]() { return this } // 实现可迭代协议
}
}
二、keys:用 MAP_KEY_ITERATE_KEY 追踪
keysIterationMethod 与 valuesIterationMethod 只差一行:追踪用的 key 不同。
function keysIterationMethod() {
const target = this.raw
const itr = target.keys()
const wrap = (val) => (typeof val === 'object' ? reactive(val) : val)
// 与 MAP_KEY_ITERATE_KEY 建立响应联系,而不是 ITERATE_KEY
track(target, MAP_KEY_ITERATE_KEY)
return {
next() {
const { value, done } = itr.next()
return { value: wrap(value), done }
},
[Symbol.iterator]() { return this }
}
}
为什么要单独一个 key:SET 操作会触发与 ITERATE_KEY 关联的副作用函数重新执行,这对 values / entries 是必需的,但对 keys 没有必要 —— keys 只关心键的变化,不关心值的变化。
于是在 trigger 中把两者分开处理:
// ADD / DELETE,或者目标对象是 Map 的 SET:触发 ITERATE_KEY 的依赖
if (
type === 'ADD' || type === 'DELETE' ||
(type === 'SET' && Object.prototype.toString.call(target) === '[object Map]')
) {
const iterateEffects = depsMap.get(ITERATE_KEY)
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect) effectsToRun.add(effectFn)
})
}
// 只有 ADD / DELETE 才触发 MAP_KEY_ITERATE_KEY 的依赖
if (type === 'ADD' || type === 'DELETE') {
const iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY)
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect) effectsToRun.add(effectFn)
})
}
效果:p.set('key', 2) 只触发 values / forEach / entries 的依赖,不会触发 keys 的依赖,从而避免不必要的更新。
三、追踪 key 一览
| 操作 / 方法 | 追踪的 key | 说明 |
|---|---|---|
size | ITERATE_KEY | 数量变化即响应 |
forEach | ITERATE_KEY | 键与值都关心 |
Symbol.iterator / entries / values | ITERATE_KEY | 值变化也要重新迭代 |
keys | MAP_KEY_ITERATE_KEY | 值变化不触发 |
get / add / delete | 具体的键 | 由 mutableInstrumentations 中的自定义方法负责 |
相关
- 5.8.5 迭代器方法:迭代器的包装与可迭代协议
- 5.8 代理 Set 和 Map:本节所属章节的总览