正文
上一篇文章我们学习了如何代理对象的读取,下面我们学习如何代理对象的修改和删除属性。
set
set就是修改代理的属性,按照我们之前写的reactive,它大概是这样的
- const ITERATE_KEY=symbol()
- const p = new Proxy(obj,{
- set(target,key,newVal,receiver){
- const res = Reflect.set(target,key,newVal,receiver)
- trigger(target,key)
- return res
- }
- }
细心的朋友应该发现了,我专门把ITERATE_KEY
也加进来了,但是在set中并没有使用,难道是多余的?
这里其实有一个小坑,就是如果曾经对对象使用过for ... in
,这里会出现2种情况:
- 如果我们新增了一个属性,那么我们是不是应该重新运行一次
for ... in
的副作用函数。 - 如果我们只是修改某个属性而不是新增,那么我们就不应该重新运行
for ... in
的副作用函数. 所以我们需要判断一下它是新增还是修改
- //定义常量,在ts中可以使用枚举
- const TriggerType = {
- SET:'SET',
- ADD:'ADD'
- }
- const p = new Proxy(obj,{
- set(target,key,newVal,receiver){
- //判断新增还是修改
- const type = Object.prototype.hasOwnProperty.call(target,key)
- ? TriggerType.SET
- : TriggerType.ADD
- const res = Reflect.set(target,key,newVal,receiver)
- trigger(target,key,type)
- return res
- }
- }
同时,对应trigger函数中,我们也需要根据type读取ITERATE_KEY
对应的副作用函数.
- const trigger = (target,key,type)=>{
- const depsMap = targetMap.get(target)
- if(!depsMap){
- return
- }
- const effects = depsMap.get(key)
- // 再次去重
- const needToRun = new Set()
- if(effects){
- effects.forEach(e=> e!==activeEffect
- ? needToRun.add(e)
- : ''
- )
- }
- if(type === TriggerType.ADD){
- const otherEffects = depsMap.get(ITERATE_KEY)
- if(otherEffects){
- otherEffects.forEach(e=> e!==activeEffect
- ? needToRun.add(e)
- : ''
- )
- }
- }
- if(needToRun.length){
- needToRun.forEach(fn=> fn?.options?.scheduler ? fn.options.scheduler(fn) : fun())
- }
- }
这样,我们只在新增的时候才会调用for ... in
的副作用函数。
delete
删除的时候,之前貌似没写过。这里需要注意2个点。
- 保证属性删除之后才运行副作用,这里从逻辑上讲我们最好先验证这个属性是否存在,避免报错。
- 删除时也要运行
for ... in
的副作用函数
因此我们这样定义,给TriggerType
新增一个类型DEL
- const TriggerType = {
- SET:'SET',
- ADD:'ADD',
- DEL:'DELETE'
- }
然后,我们开始拦截删除属性的操作,查一下之前的Proxy
内部方法的表,我们可以得知,删除属性对应着deleteProperty
方法。
- const p = new Proxy(obj,{
- deleteProperty(target,key){
- //判断属性存在,你总不能删除一个不存在的属性吧
- const hadKey = Object.prototype.hasOwnProperty.call(target,key)
- const res = Reflect.deleteProperty(target,key)
- if(res && hadKey){
- trigger(target,key,TriggerType.DEL)
- }
- return res
- }
- }
对应trigger函数中,我们小修改一下,其他逻辑不变
- // 删除这句
- - if(type === TriggerType.ADD){
- // 改为
- + if([TriggerType.ADD,TriggerType.DEL].includes(type)){
这样就可以实现响应式对象的删除属性。
其实原文中并没有使用Array.includes
,但我觉得其实我们应该使用最新的语法,现在浏览器环境对这些新语法支持度已经很好了(如果你要兼容IE当我没说)。
这一篇就完结了,总结一下就是如何对对象的读取属性、修改属性、删除属性进行代理,大概了解vue3中对于对象的处理。
但是这里还没有结束,后续会讲一些边际条件,以及如何合理的响应数据变化和操作,合理也就是优化,尽可能的减少多余的响应。
更多关于vue响应式Object修改删除的资料请关注w3xue其它相关文章!