【vue设计与实现】非原始值的响应式方案 11-代理Set和Map-建立响应联系

现在可以着手实现Set类型数据的响应式方案了。以下面的代码为例:

const p = reactive(new Set([1,2,3]))
effect(()=>{
	// 在副作用函数内访问size属性
	console.log(p.size)	
})
//增加值为1的元素,应该触发响应
p.add(1)

p.add函数向集合中添加数据,这个行为会间接改变集合size的属性值,所有期望是副作用函数会重新执行。为了实现效果,需要在访问size时调用track函数进行依赖追踪,然后在add方法执行时调用trigger函数触发响应
下面代码展示如何进行依赖追踪

function createReactive(obj, isShallow = false, isReadonly = false){
	return new Proxy(obj, {
		get(target, key, receiver){
			if(key === 'size'){
				// 调用track函数建立响应联系
				track(target, ITERATE_KEY)
				return Reflect.get(target, key, target)
			}
			return target[key].bind(target)
		}
	})
}

这里需要注意,响应联系需要建立在ITERATE_KEY于副作用函数之间,因为任何新增、删除操作都会影响size。

接下来看如何触发响应,显然我们需要实现一个自定义的add方法才行,如下代码:

// 定义一个对象,将自定义的add方法定义到该对象下
const mutableInstrumentations = {
	add(key){/*...*/}
}
function createReactive(obj, isShallow = false, isReadonly = false){
	return new Proxy(obj, {
		get(target, key, receiver){
			if(key === 'raw') return target 
			if(key === 'size'){
				// 调用track函数建立响应联系
				track(target, ITERATE_KEY)
				return Reflect.get(target, key, target)
			}
			// 返回定义在mutableInstrumentations对象下的方法
			return mutableInstrumentations[key]
		}
	})
}

在这里首先定义一个对象,将所有自定义实现的方法都定义到该对象下,然后,在get拦截函数内返回。这样通过p.add获取方法时,得到的是自定义的mutableInstrumentations.add方法。有了自定义实现的方法后,就可以在其中调用trigger函数触发响应了。

const mutableInstrumentations = {
	add(key){
		// this仍然指向的是代理对象,通过raw属性获取原始数据对象
		const target = this.raw
		// 通过原始数据对象执行add方法
		// 注意这里不需要使用bind方法了,因为是直接通过target调用
		const res= target.add(key)
		// 调用trigger函数触发响应,指定操作类型为ADD
		trigger(target, key, 'ADD')
		// 返回操作结果
		return res
	}
}

而且当调用add方法添加的元素已经存在于Set集合中,就不再需要触发响应了,这样对性能更友好,因此可以对add方法进行优化

const mutableInstrumentations = {
	add(key){
		const target = this.raw
		// 先判断值是否一斤存在
		const hadKey = target.has(key)
		// 只有在值不存在的情况下,才需要触发响应
		if(!hadKey){
			const res = target.add(key)
			trigger(target, key, 'ADD')
		}
		return res
	}
}

在实现add的思路上可以很轻松地实现delete方法

const mutableInstrumentations = {
	delete(key){
		const target = this.raw
		const hadKey = target.has(key)
		const res = target.delete(key)
		// 当要删除的元素确实存在时,才触发响应
		if(hadKey){
			trigger(target, key, 'DELETE')
		}
		return res
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值