【vue设计与实现】非原始值的响应式方案 6-只读和浅只读

只读要达到的效果是,当用户尝试修改只读数据时,会收到一条警告信息。这样就实现了对数据的保护。这时就需要一个readonly函数,能够将一个数据变成只读的:

const obj = readonly({foo:1})
// 尝试修改数据,会收到警报
obj.foo = 2

只读本质上就是对数据对象的代理,同样可以用createReactive函数来实现。这时要为createReactive函数增加第三个参数isReadonly

// 增加第三个参数isReadonly
function createReactive(obj, isShallow = false, isReadonly = false){
	return new Proxy(obj, {
		set(target, key, newVal, receiver){
			// 如果是只读的就打印警告信息并返回
			if(isReadonly){
				console.warn(`属性${key}是只读的`)
				return true
			}
			const oldVal = target[key]
			const type = Object.prototype.hasOwnProperty.call(target, key)?"EDIT":"ADD"
			const res = Reflect.set(target, key, newVal, receiver)
			if(target === receiver.raw){
				if(oldVal !== newVal && (oldVal === oldVal || newVal === newVal)){
					trigger(target, key, type)
				}
			}
			return res
		},
		deleteProperty(target,key){
			// 如果是只读的就打印警告信息并返回
			if(isReadonly){
				console.warn(`属性${key}是只读的`)
				return true
			}
			const hasKey = Object.prototype.hasOwnProperty.call(target, key)
			const res = Reflect.deleteProperty(traget, key)
			if(res && hadKey){
				trigger(target, key, 'DELETE')
			}
			return res
		}
	})
}

上面代理同时修改了get拦截函数和deleteProperty拦截函数,因为对一个对象来说,只读意味着既不可以设置对象的属性值,也不可以删除对象的属性。
当然如果一个数据是只读的,也就意味着没有必要为只读数据建立响应联系。因此在副作用函数中当读取一个只读属性的值时,不需要调用track函数追踪响应
为了实现这个功能,需要修改get拦截函数的实现

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)
			}
			...
		}
	})
}

这样我们就实现了readonly函数了

function readonly(obj){
	return createReactive(obj, false, true)
}

但上面实现的只是浅只读——shallowReadonly

要实现深只读,还应该在get函数内递归地调用readonly将数据包装成只读的代理对象,并将其作为返回值返回

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 === 'objcet' && res !== null){
				// 如果数据为只读,则调用readonly对值进行包装
				return isReadonly?readonly(res):reactive(res)
			}
			return res
		}
	})
}

所以对于shallowReadonly来说,实际上只需要修改createReactive的第二个参数即可

function readonly(obj){
	return createReactive(obj, false, true)
}
function shallowReadonly(obj){
	return createReactive(obj, true, true)
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值