5.5 浅响应与深响应
同一套拦截逻辑要能同时支持「嵌套属性也可响应」和「只有第一层可响应」两种模式。
一、两个概念
| 模式 | 含义 | 表现 |
|---|---|---|
| 深响应 | 修改对象的嵌套属性也能触发响应 | obj.foo.bar = 3 能触发 |
| 浅响应 | 只有对象的第一层属性是响应的 | obj.foo.bar = 3 不能触发 |
二、实现:用 isShallow 参数区分
把 reactive 改成通用的 createReactive(obj, isShallow = false):
function createReactive(obj, isShallow = false) {
return new Proxy(obj, {
get(target, key, receiver) {
if (key === 'raw') return target
const res = Reflect.get(target, key, receiver)
track(target, key) // 建立联系
// 浅响应:直接返回原始值,不做任何包装
if (isShallow) return res
// 深响应:属性值是对象时,递归包装成响应式数据
if (typeof res === 'object' && res !== null) {
return reactive(res)
}
return res
},
// set / has / ownKeys / deleteProperty 与 5.4 相同
})
}
// 深响应
function reactive(obj) {
return createReactive(obj)
}
// 浅响应
function shallowReactive(obj) {
return createReactive(obj, true)
}
要点:
- 深响应:读取属性值时先判断结果是否为对象,是对象就递归调用
reactive包装后返回,这样嵌套属性被访问时也会走get拦截器并收集依赖; - 浅响应:
isShallow为真时直接返回原始数据,嵌套对象不会被代理,自然无法触发响应。
三、验证
const obj = reactive({ foo: { bar: 1 } })
effect(() => {
console.log(obj.foo.bar)
})
obj.foo = { bar: 2 } // 能触发:foo 是第一层属性
obj.foo.bar = 3 // 深响应下也能触发(浅响应下不能)
obj.foo返回的是代理对象,所以对它取bar会经过get拦截器;而浅响应下obj.foo返回的是原始对象,对它赋值不会经过任何拦截器。
相关
- 5.4 合理地触发响应:
createReactive中 set 部分的完整实现 - 5.6 只读和浅只读:在
createReactive上再叠加isReadonly