set
set ⽅法接收 3个参数, target 可能是数组或者是对象, key 代表的是数组的下标或者是对象的键值, val 代表添加的值。⾸先判断如果 target 是数组且 key 是⼀个合法的下标,则调用数组的splice,这⾥的 splice 其实已经不仅仅是原⽣数组的 splice 了,后面说到数组就清楚了。接着⼜判断 key 是否存在target 中,是直接赋值返回,存在说明已经是响应式数据了。接着再获取到 target.ob 并赋值给 ob ,之前分析过它是在 Observer 的构造函数执⾏的时候初始化的,表⽰ Observer 的⼀个实例,如果它不存在,则说明 target 不是⼀个响应式的对象,则直接赋值并返回。最后通过 defineReactive(ob.value, key, val) 把新添加的属性变成响应式对象,然后再通过 ob.dep.notify() ⼿动的触发依赖通知,回忆以下之前在给对象添加get的时候有这么⼀段逻辑:
在get过程中判断了childOb ,并调⽤了childOb.dep.depend() 收集了依赖,这就是为什么执⾏Vue.set的时候通过 ob.dep.notify()能够通知到 watcher ,从⽽让添加新的属性到对象也可以检测到变化。这⾥如果 value 是个数组,那么就通过 dependArray 把数组每个元素也去做依赖收集。
数组更新
接着说⼀下数组,Vue是不能检测到以下变动的数组:
1.当利⽤索引直接设置修改⼀个项时,例如: vm.items[0] = xxx
2.当修改数组的⻓度时,例如: vm.items.length = 20
对于第⼀种情况,可以使⽤: Vue.set(this.items, 0, xxx)或者vm.items.splice(0, 1 ,xxx)
对于第⼆种 情况,可以使⽤ vm.items.splice(xx) 。
我们刚才也分析到,对于 Vue.set 的实现,当 target 是数组的时候,也是通过 target.splice(key, 1, val) 来添加的,那么这⾥的 splice做了啥,能让添加的对象变成响应式的呢。之前我们也说过,在通过 observe ⽅法去观察对象的时候会实例化 Observer ,在它的构造函数中是有专门对数组做了处理。
这⾥的hasProto 实际上就是判断对象中是否存在 proto ,如果存在则调用protoAugment,否则调用 copyAugmen。
protoAugment ⽅法是直接把 target.proto 原型直接修改为 src,实际上就把value的原型指向了arrayMethods。
copyAugment⽅法是遍历 keys,通过 def ,也就是 Object.defineProperty 去定义它⾃⾝的属性值。
可以看到, arrayMethods ⾸先继承了Array,然后对数组中所有能改变数组⾃⾝的⽅法,如 push、pop 等这些⽅法进⾏重写。重写后的⽅法会先执⾏它们本⾝原有的逻辑,并对能增加数组⻓度 的 3 个⽅法 push、unshift、splice ⽅法做了判断,获取到插⼊的值,然后把新添加的值变成⼀个响应式对象,并且再调⽤ ob.dep.notify() ⼿动触发依赖通知,这就很好地解释了之前的⽰例中调 ⽤ vm.items.splice(2) ⽅法可以检测到变化。
总结
1.set⽅法接收 3个参数,target可以是数组或者是对象,key代表的是数组的下标或者是对象的键值,val代表添加的值,首先对targrt类型和判空判断不是数组或者对象类型或者值为undefined则报错。
2.如果目标是数组且索引有效那么就修改数组长度替换值。
3.如果目标是对象那么会先判断key是否已经存在目标对象中,如果存在说明key在初始化时已经做了响应式的处理所以直接替换值返回即可。如果不存在目标对象中,尝试拿到目标对象的ob__属性,如果没有_ob_ 说明目标对象不是响应式对象,不需要响应,那么直接赋值返回即可。否则目标对象存在__ob属性则调用defineReactive把新添加的属性变成响应式对象,最后调用ob.dep.notify()手动触发依赖通知。
4.在初始化对data进行处理时,如果data为数组会执行protoAugment重写数组的原型方法。设置对象的原型为arrayMethods,抛出新的方法让数据(data)的原型等于我们抛出的方法,从而操作数组时先找到原型我们抛出的方法执行,抛出的方法除了数组方法能做的事外还对数据进行了劫持监听以及手动通知强制渲染刷新。
5.arrayMethods首先获取数组原型然后拷贝一份,接着定义了数组操作的七个方法。遍历七个方法首先获取原始数组的方法,紧着着调用def对拷贝的数组的每个方法做以下处理,首先执行原始的方法操作,其次获取到当前对象的ob,如果方法的操作是插入操作 还需对其做响应式处理,最后通过ob.dep.notify()手动触发依赖通知。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!