Vue源码探索之$set实现

113 阅读1分钟

文件路径src\core\observer\index.js

需要传入target/key/val

1、判断是否是undefined或者是否设为原始数据类型,如果是,抛出警告

if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
 ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
 }

2、判断target是否为数组,以及key是否是合法的索引,如果满足条件,调用数组的splice()方法插入数据(ps:因为Vue里面对数组的push/splice等方法进行了修补,使传入的数据都是响应式的)

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
  }

3、以上流程没有return的话,认为target是一个对象,判断key是否是target中已经存在的值,如果存在,直接改变key对应的val,并返回val

// 如果 key 在对象中已经存在直接赋值
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }

4、如果是新增的属性,获取target__ob__属性,判断target是否为Vue实例或者$data,如果是,直接返回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
  }

5、判断是否存在ob,如果不存在,则target不是一个响应式的对象,直接赋值,不需要将key转为响应式,如果ob存在,则调用defineReactive(ob.value, key, val)将新增的属性转为响应式数据,并调用ob.dep.notify()发送通知

 // 如果 ob 不存在,target 不是响应式对象直接赋值
  if (!ob) {
    target[key] = val
    return val
  }
  // 把 key 设置为响应式属性
  defineReactive(ob.value, key, val)
  // 发送通知
  ob.dep.notify()
  return val