6.vue.set是怎么实现的

53 阅读2分钟

有六种情况

1.target未定义或者是基础类型抛出警告

// 1.target未定义或或者是基础类型就抛出警告
if (isUndef(target) || isPrimitive(target)) {
  warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target))));
}

2.判断是否为数组,并判断key是不是有效索引,将数组的.length属性设置为最大值调用数组的splice方法,将修改的数据变为响应式

// 2.判断是否为数组,并判断key是不是有效索引,
if (Array.isArray(target) && isValidArrayIndex(key)) {
  // 判断下标和数组长度,将数组的.length属性设置为最大值
  target.length = Math.max(target.length, key);
  // 调用数组的splice方法,将修改的数据变为响应式
  target.splice(key, 1, val);
  // 返回对应的val
  return val
}

3.如果这个属性在这个对象上,则设置属性为对应的属性值直接返回val

// 3.如果这个属性在这个对象上,则设置属性为对应的属性值后直接返回val
if (key in target && !(key in Object.prototype)) {
  target[key] = val;
  return val
}

4.判断当前的目标对象是否是Vue实例,或者根数据对象,如果是则warn警告并返回(更新data无意义)

// 在getter的时候设置__ob__,获取响应式对象__ob__
var ob = (target).__ob__;
// 4.判断当前的目标对象是否是Vue实例,或者根数据对象,如果是则warn警告并返回
if (target._isVue || (ob && ob.vmCount)) {
  warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
  );
  return val
}
  1. 当前的这个目标对象如果不是响应式对象则直接赋值返回
//5. 当前的这个目标对象如果不是响应式对象则直接赋值返回
if (!ob) {
  target[key] = val;
  return val
}

6.对象更新,defineReactive添加响应式属性,调用update,更新渲染视图

// 给目标对象对应的属性添加响应式
defineReactive(ob.value, key, val);
// 通知所有订阅者,调用update,更新渲染视图
ob.dep.notify();
return val

当新增属性时可以考虑对象合并的方法

this.obj={...this.obj,...{a:1,b:2}}

总结:

当set所设置的目标对象为数组时,则调用目标对象的splice方法将修改的数据变为响应式

当set所设置的目标对象为对象时,首先判断这个属性是否在这个对象上,如果存在则设置属性为对应的属性值后直接返回val,然后判断目标对象是否为Vue实例或者根数据对象,如果是则warn警告后返回,再去判断这个目标对象是否是响应式的,如果不是响应式对象则直接赋值返回。最后在给目标对象的属性添加响应式,通知dep实例的所有订阅者进行更新