Vue.js 源码分析 - set-源码

399 阅读2分钟

「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战

Vue.js 源码分析 - set-源码

定义位置

  • Vue.set() -构造函数的中的方法,也就是静态方法
    • global-api/index.js
      // 静态方法 set/delete/nextTick
      Vue.set = set
      Vue.delete = del
      Vue.nextTick = nextTick
    
  • vm.$set() - 实例中的方法
    • instance/index.js
      // 注册 vm 的 $data/$props/$set/$delete/$watch
      stateMixin(Vue)
      
    • instance/state.js
      Vue.prototype.$set = set
      Vue.prototype.$delete = del
      

从上面的代码进行分析,我们可以看见Vue.set() vm.$set()都设置成了 set 方法,接下来,我们就来查看set函数是如何实现的

set 函数的实现 - 解析

  1. 代码模块1

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

    解析:isUndef(target) || isPrimitive(target) 这里的判断数据是否undefinedor原始值进行判断,如果是的话,就抛出错误

  2. 代码模块2

    if (Array.isArray(target) && isValidArrayIndex(key)) {
        target.length = Math.max(target.length, key)
        target.splice(key, 1, val)
        return val
      }
    

    解析:

    • Array.isArray(target) && isValidArrayIndex(key): 判断 target 是否是对象,key 是否是合法的索引
    • target.splice(key, 1, val): 通过 splice 对key位置的元素进行替换,splice 在 array.js 进行了响应化的处理
  3. 代码模块3

    if (key in target && !(key in Object.prototype)) {
        target[key] = val
        return val
      }
    

    解析: 如果 key 在对象中已经存在直接赋值,不再做响应式的处理了

  4. 代码模块4

     const ob = (target: any).__ob__
    

    解析:之前我们也看过响应式的处理,我们在处理完毕数据之后会给数据添加上__ob__属性,这个__ob__中存储的值就是 observer对象

  5. 代码模块5

    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 是 vue 实例或者 $data 直接返回

  6. 代码模块6

     if (!ob) {
        target[key] = val
        return val
      }
      }
     
      defineReactive(ob.value, key, val)
      ob.dep.notify()
    

    解析: - 判断 ob 是否存在,如果 ob 不存在,target 不是响应式对象直接赋值 - defineReactive(ob.value, key, val) 把 key 设置为响应式属性 - ob.dep.notify() 发送通知

小结

ok,这就是 set 函数内部的实现

  • set可以处理数组的响应式:当使用set给数组设置值的时候,会调用 splice 方法
  • set可以处理对象的响应式: 当给对象设置新的值的时候,会调用 defineReactive方法 最终会调用 ob.dep.notify() 发送通知