set

Vue.set、vm.$set 都指向了内部的 set 方法。它们的功能是向响应式对象中添加属性,并确保这个对象是响应式的,且触发视图更新。它必须用于向响应式对象添加新属性,因为 Vue 无法探测普通的新增属性(比如 this.myObject.newProperty = ‘hi’)
注意:传入的对象不能是 Vue 实例,或者 Vue 实例的根数据对象(比如 Vue.$data)。

  1. vm.$set(obj, 'foo', 'test')

定义位置

  • Vue.set()

    • global-api/index.js
      1. // 静态方法 set/delete/nextTick
      2. Vue.set = set
      3. Vue.delete = del
      4. Vue.nextTick = nextTick
  • vm.$set()

    • instance/index.js
      1. // 注册 vm 的 $data/$props/$set/$delete/$watch
      2. // instance/state.js
      3. stateMixin(Vue)
      4. // instance/state.js
      5. Vue.prototype.$set = set
      6. Vue.prototype.$delete = del

源码

  • set() 方法
    • observer/index.js ```javascript /**
    • Set a property on an object. Adds the new property and
    • triggers change notification if the property doesn’t
    • already exist. */ export function set (target: Array | Object, key: any, val: any): any { if (process.env.NODEENV !== ‘production’ && (isUndef(target) || isPrimitive(target)) ) { warn(Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}) } // 判断 target 是否是对象,key 是否是合法的索引 if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key) // 通过 splice 对key位置的元素进行替换 // splice 在 array.js 进行了响应化的处理 target.splice(key, 1, val) return val } // 如果 key 在对象中已经存在直接赋值 if (key in target && !(key in Object.prototype)) { target[key] = val return val } // 获取 target 中的 observer 对象 const ob = (target: any)._ob // 如果 target 是 vue 实例或者 $data 直接返回 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 } // 如果 ob 不存在,target 不是响应式对象直接赋值 if (!ob) { target[key] = val return val } // 把 key 设置为响应式属性 defineReactive(ob.value, key, val) // 发送通知 ob.dep.notify() return val } ```

del

Vue.delete、vm.$delete 都指向了内部的 del 方法。它们的功能是删除对象的属性,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到属性被删除的限制。
注意:传入的对象不能是 Vue 实例,或者 Vue 实例的根数据对象(比如 Vue.$data)。

  1. vm.$delete(vm.obj, 'msg')
  2. vm.$delete(vm.arr, 0) // 删除数组中的第一个元素

定义位置

  • Vue.delete()

    • global-api/index.js
      1. // 静态方法 set/delete/nextTick
      2. Vue.set = set
      3. Vue.delete = del
      4. Vue.nextTick = nextTick
  • vm.$delete()

    • instance/index.js
      1. // 注册 vm 的 $data/$props/$set/$delete/$watch
      2. stateMixin(Vue)
      3. // instance/state.js
      4. Vue.prototype.$set = set
      5. Vue.prototype.$delete = del

源码

  • src\core\observer\index.js
    1. /**
    2. * Delete a property and trigger change if necessary.
    3. */
    4. export function del (target: Array<any> | Object, key: any) {
    5. if (process.env.NODE_ENV !== 'production' &&
    6. (isUndef(target) || isPrimitive(target))
    7. ) {
    8. warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
    9. }
    10. // 判断是否是数组,以及 key 是否合法
    11. if (Array.isArray(target) && isValidArrayIndex(key)) {
    12. // 如果是数组通过 splice 删除
    13. // splice 做过响应式处理
    14. target.splice(key, 1)
    15. return
    16. }
    17. // 获取 target 的 ob 对象
    18. const ob = (target: any).__ob__
    19. // target 如果是 Vue 实例或者 $data 对象,直接返回
    20. if (target._isVue || (ob && ob.vmCount)) {
    21. process.env.NODE_ENV !== 'production' && warn(
    22. 'Avoid deleting properties on a Vue instance or its root $data ' +
    23. '- just set it to null.'
    24. )
    25. return
    26. }
    27. // 如果 target 对象没有 key 属性直接返回
    28. if (!hasOwn(target, key)) {
    29. return
    30. }
    31. // 删除属性
    32. delete target[key]
    33. if (!ob) {
    34. return
    35. }
    36. // 通过 ob 发送通知
    37. ob.dep.notify()
    38. }