在vue.js中,vm.$set是一个比较常用的API,先简单回顾一下用法。

一.用法:
vm.$set(target, key, value)
参数:
{object | Array} target
{string | number} key
{any} value
返回值:{function} unwatch
用法:在object上设置一个属性,如果object是响应式的,vue.js会保证属性被创建后也是响应式的,并且触发视图更新。这个方法主要是用来避开vue.js不能侦测属性被添加的限制。
注意:target不能是vue.js实例或者vue.js实例的根数据对象。
了解了变化侦测原理,我们知道了只有已经存在的属性的变化才会被追踪到,新增的属性无法被追踪到。因为在ES6之前,javascript并没有提供元编程的能力,所以根本无法侦测object什么时候被添加了一个新的属性。而vm.$set就是为了解决这个问题而出现的,使用它可以为Object新增属性,然后vue.js就可以将这个新增属性转换成响应式的。
问题是怎样出现的呢?举个例子:
data中有一个obj对象,如果直接给obj设置一个属性,例如:
在action方法被调用时,会为obj新增一个name属性,而vue.js不会得到任何通知,新增的这个属性也不是响应式的,vue.js根本不知道这个obj新增了属性,就好像vue.js无法知道我们使用了array.length=0清空了数组一样。
vm.$set就可以解决这个事情,我们来看看vm.$set是如何实现的:
import {set} from '../observer/index'
Vue.prototype.$set = set;
这里我们在vue.js的原型上设置了
$set属性,其实所有以vm.$开头的方法都是在vue.js的原型上设置的,vm.$set的具体实现其实是在observer中抛出的set方法。所以我们先创建一个set方法。
export function set(target, key, val){}
二.set方法对于target是Array的处理
在上面的代码中:
- ①如果target是数组并且key是一个有效的索引值,就先设置length属性。②如果传递的索引值key大于当前数组的lenght,就让target的length等于索引值。
- 通过splice方法把val设置到target中的指定位置(参数中提供的索引值的位置)
- 当我们使用splice方法把val设置到target中的时候,数组拦截器会侦测到target发生了变化,并且会自动帮助我们把这个新增的val转换成响应式的。
- 最后返回val即可
三.set方法对于target是Object的处理
如果target是obejct,则对于key分为俩种情况:
①key已经存在于target中
②key不存在target中,而是新增的属性。
1.key已经存在于target中
因为key已经存在于target中,所以其实这个key已经被侦测了变化。也就是说,这种情况属于修改数据,直接用key和val改数据就好了。修改数据的动作会被vue.js侦测到,所以数据发送变化后,会自动向依赖发送通知。
2.key不存在target中,而是新增的属性
- 在上面的代码中,最先获得target的
__ob__属性;- 判断target是否是vue.js实例或vue.js实例的根数据对象。使用target._isVue来判断是不是vue.js实例。使用ob.vmCount来判断它是不是根数据对象。什么是根数据呢?
this.$data就是根数据。- 接下来处理target不是响应式的情况。如果target身上没有
__ob__属性,说明它并不是响应式的,不需要做什么特殊处理,只需要通过key和val在target上设置。- 如果上面条件都不满足,那么说明用户是在响应式数据上新增了一个属性,这种情况下需要追踪这个新属性的变化,即使用defineReactive将新增属性转换为getter/setter的形式即可。
- 最后向target的依赖触发变化的通知,并返回val.
本文详细探讨了Vue.js中vm.$set的用法和实现原理。针对Array和Object两种情况,分析了如何确保新增属性成为响应式的,并通过setter触发视图更新。对于Array,vm.$set利用splice方法确保元素被正确添加并保持响应性;对于Object,当属性不存在时,通过defineReactive将其转换为响应式属性。







被折叠的 条评论
为什么被折叠?



