$set

用法

set 是全局 Vue.set 的别名。

set(target, propertyName/index, value)

作用

Vue 中对于对象新增的属性或者是数组新增的下标都是不能够监听到的,为了保障新的属性同样是响应式的,且触发视图更新,需要借助 this.$set 来新增属性。

对象不能是 Vue 实例或者是 Vue 实例的根数据对象。

原理

// src/core/observer/index.js

function set (target: Array<any> | Object, key: any, val: any): any {
  // 判断非生产环境下传入的 target 是否为 undefined、null 或原始类型
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    // 如果是,就抛出警告
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  // 如果传入的是数组 并且 key 是有效的数组索引。
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 取数组长度和 key 这两者最大的值作为数组的新长度
    target.length = Math.max(target.length, key)
    // 数组的splice方法将传入的索引key对应的val值添加进数组
    target.splice(key, 1, val)
    // 把值返回
    return val
  }
  // 如果是对象
  //首先判断 key 是否存在于 target 中。
  if (key in target && !(key in Object.prototype)) {
    // 修改操作, 只修改属性值即可。
    target[key] = val
    return val
  }
  // __ob__ 标志着是否已经是响应式的了。
  const ob = (target: any).__ob__
  // target 是否是 Vue 实例,或者 vue 实例的根数据对象
  if (target._isVue || (ob && ob.vmCount)) {
    // 抛出警告
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    // 退出程序
    return val
  }
  // target 不是一个响应式对象
  if (!ob) {
    // 只需要给 target 添加属性
    target[key] = val
    return val
  }
  // 是对象,并且是响应式的,将新属性添加到 target 上。
  defineReactive(ob.value, key, val)
  // 通知依赖跟新
  ob.dep.notify()
  // 返回值
  return val
}

可以看出来,就是通过不断的判断,先判断 target 的类型是否为 null、undefined、原始数据类型。然后判断是否为数组,然后判断是否为对象.

$delete

vm.$delete 是全局 Vue.delete 别名。

vm.$delete(target, propertyName, index)

删除对象的属性,能够删除后触发视图的更新。属性被删除是不能够被检测到的。

源码

function del (target: Array<any> | Object, key: any) {
  // 判断非生产环境下传入的 target 是否为 undefined、null 或原始类型
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    // 警告
    warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  // 如果 target 是数组, 并且 key 是有效索引
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 使用 splice 来删除掉 key 的数组元素。
    target.splice(key, 1)
    return
  }
  // __ob__ 标志着是否已经是响应式的了。
  const ob = (target: any).__ob__
  // target 是否是 Vue 实例,或者 vue 实例的根数据对象
  if (target._isVue || (ob && ob.vmCount)) {
    // 警告
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid deleting properties on a Vue instance or its root $data ' +
      '- just set it to null.'
    )
    return
  }
  // key 是否存在于 target 上, 不存在就不用删除
  if (!hasOwn(target, key)) {
    return
  }
  // 从属性中删除
  delete target[key]
  // 当前的 target 是否是响应式对象
  if (!ob) {
    // 如果不是响应式就删除后直接返回
    return
  }
  // 是响应式的,就通知依赖更新
  ob.dep.notify()
}