5.6 只读和浅只读
有些数据(如组件接收的 props)不允许被修改,需要在拦截层拒绝写入并给出警告。
一、只读意味着什么
- 只读数据既不可以设置属性值,也不可以删除属性;
- 既然任何方式都无法修改它,就没有必要为只读数据建立响应联系(不执行
track)。
props对象应该是一个只读数据:即使修改也不会生效,但要让用户收到明确的警告。
二、在 createReactive 上叠加 isReadonly
只需给 createReactive 增加第三个参数,并在三个拦截器里各加一处判断:
function createReactive(obj, isShallow = false, isReadonly = false) {
return new Proxy(obj, {
get(target, key, receiver) {
if (key === 'raw') return target
// 非只读时才需要建立响应联系
if (!isReadonly) track(target, key)
const res = Reflect.get(target, key, receiver)
if (isShallow) return res
if (typeof res === 'object' && res !== null) return reactive(res)
return res
},
set(target, key, newVal, receiver) {
// 只读:打印警告并直接返回
if (isReadonly) {
console.warn(`属性 ${key} 是只读的`)
return true
}
// ...(5.4 的新旧值比较、trigger 逻辑)
},
has(target, key) {
track(target, key)
return Reflect.has(target, key)
},
ownKeys(target) {
track(target, ITERATE_KEY)
return Reflect.ownKeys(target)
},
deleteProperty(target, key) {
// 只读:打印警告并直接返回
if (isReadonly) {
console.warn(`属性 ${key} 是只读的`)
return true
}
// ...(hadKey 判断 + trigger(target, key, 'DELETE'))
}
})
}
function reactive(obj) { return createReactive(obj) }
function shallowReactive(obj) { return createReactive(obj, true) }
function readonly(obj) { return createReactive(obj, false, true) }
function shallowReadonly(obj) { return createReactive(obj, true, true) }
只读的
set/deleteProperty返回true表示「操作成功」,因为拦截器本身不应报错,只需警告。
三、深只读
上面的 readonly 其实只能叫 shallowReadonly:嵌套对象仍是可改的。
const obj = readonly({ foo: 1 })
obj.foo = 2 // 警告
const obj2 = readonly({ foo: { bar: 2 } })
obj2.foo.bar = 3 // 仍然可以修改,说明需要「深只读」
做法:在 get 中递归包装时,按 isReadonly 选择用 readonly 还是 reactive:
if (typeof res === 'object' && res !== null) {
// 只读数据递归包装成只读,否则包装成响应式
return isReadonly ? readonly(res) : reactive(res)
}
const obj = readonly({ foo: { bar: 1 } })
obj.foo.bar = 2 // 抛出警告
四、四种模式一览
| 函数 | isShallow | isReadonly | 说明 |
|---|---|---|---|
reactive | false | false | 深响应 |
shallowReactive | true | false | 浅响应 |
readonly | false | true | 深只读 |
shallowReadonly | true | true | 浅只读 |
相关
- 5.5 浅响应与深响应:
createReactive与isShallow的由来 - 5.7 代理数组:换一种数据结构,哪些读写操作需要额外处理