Skip to content

set

javascript
// 向响应式对象中添加一个 property, 并确保这个新 property 同样是响应式的, 
// 且触发视图更新。
// 如果 target 是基本数据类型,直接警告。如果是数组并且索引合法,通过 splice 添加,
// 添加完后会更新视图。否则判断对象自身中是否有该属性,如果有该属性直接重写即可,
// 此时如果该对象是响应式的就出发视图更新,如果不是响应式的也不管了。
// 如果没有该属性说明是往对象中添加属性,先获取 target 的 ob,如果 target._isVue
// 为真或 ob.vmCount 为真说明要操作的对象是 vm 或 data,直接警告。
// ob 是否为假,为假说明这个对象不是响应式的,直接添加属性即可。
// 否则说明对象是响应式的,通过 defineReactive 为对象定义一个响应式属性。
// 最后调用 ob.dep.notify() 更新视图。
Vue.set = function set (target, key, val) {
  // Vue.set(obj.a, 'b', 'c') 

  if (isUndef(target) || isPrimitive(target)) {
    // warn
  }
    
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key);
    target.splice(key, 1, val) // splice是变更方法
    return val
  }
  
  if (key in target && !(key in Object.prototype)) { // target.hasOwnProperty(key)
    target[key] = val // target上已经有key属性了, 直接重写即可
    return val
  }
    
  var ob = (target).__ob__; // 获取obj.a.__ob__
    
  if (target._isVue || (ob && ob.vmCount)) { // target是Vue实例或vm._data
    // warn
    return val
  }
    
  if (!ob) {
    // target不是响应式的, 直接添加属性
    target[key] = val;
    return val
  }
    
  // 设置响应式
  defineReactive(ob.value, key, val) // Observer构造器中this.value = value
    
  // 更新视图
  ob.dep.notify()
    
  return val
}

function defineReactive(
  obj: object,
  key: string,
  val?: any
) {
  // 每个属性收集使用它们的 watcher
  const dep = new Dep()

  // 需要实现数组或对象所有层次的对象的属性的劫持
  // childOb 是 Observe 实例, val 是数组或对象时 childOb 才有值
  let childOb = observe(val, false, mock)
  // 为该属性实现响应式
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const value = val
      // 如果模板外使用数据,不进行收集操作。
      if (Dep.target) {
        // 互相收集
        dep.depend()
        // value 是数组或对象
        if (childOb) {
          // 数组变更方法响应式 & Vue.set() 所有层次属性的响应式
          childOb.dep.depend()
          // 数组嵌套时内部数组变更方法响应式
          if (isArray(value)) {
            dependArray(value)
          }
        }
      }
    }
  })
}