Vue3 reactive api 解析

先写一个简单的reactive,就是返回了一个proxy

    function reactive(target: object) {
        return new Proxy(target, {
            get() {
                track() // 收集依赖,下面会讲
            },
            set() {
                trigger() // 通知依赖进行更新,下面会讲
            },
            has() {},
            ownKeys() {}
            
            // ....
        })
    
    }
    
    // 通过 reactive转了之后,每次使用的属性的地方(target.xxx),都会触发get,get就会收集依赖.依赖简单来说就是,你在watchEffect里面使用了 target.xx, 这个watchEffect就是一个依赖(watchEffect传入的回调函数)
    
    // 修改target.xxx, 就会触发 set,set里面会trigger,trigger就是通知依赖进行更新,上面收集了watchEffect的回调函数,这里再次让回调函数执行。达到了修改一个地方,其他地方跟着一起更新

复制代码

源码部分(自己做了一部分删减,核心部分还是不变的,目的是想更好的让大家理解):

function reactive(target: object) {
 
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) { // 传了一个readOnly的proxy,就会直接返回这个对象,不进行下一部
    return target
  }
  return createReactiveObject(  // 生成响应式对象,这里的 mutableHandlers 就是对应的我在上面写的 get,set,has, ownkeys的方法 
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers // 这个是针对Map,Set。暂时不管
  )
}

// createReactiveObject
function createReactiveObject(
  target: Target, // 传入的对象
  isReadonly: boolean, // false
  baseHandlers: ProxyHandler<any>, // get,set 一系列方法
  collectionHandlers: ProxyHandler<any> // 针对Map,Set,暂时不管
) {
  if (!isObject(target)) { // reactive 只接收对象
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  
  // exception: calling readonly() on a reactive object
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target  // 简单来说,就是当你reactive(reactiveObject)再次传入一个reactive时,不会重新实例化一个proxy,会直接返回当前对象
    
    // 这里还需要关注一个点,当target已经是一个reactive时,这里方法 target[ReactiveFlags.RAW],就会触发这个target的 get,因为它已经是一个proxy了
  }
   
  const proxyMap = isReadonly ? readonlyMap : reactiveMap
  const existingProxy = proxyMap.get(target) // 传入一个相同的对象时,直接在全局的rroxyMap里面拿到值了,不会重新实例化
  if (existingProxy) {
    return existingProxy
  }
  
  const targetType = getTargetType(target) // 基本都是合法的的类型,都是返回的 TARGET.COMMON
  if (targetType === TargetType.INVALID) {
    return target
  }
  const proxy = new Proxy( // 实例化proxy
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy) // 做了一个WeakMap的缓存,key就是这个对象,值是proxy,目的就在上面,见existingProxy的解析
  return proxy


// 以下是模拟的 baseHandlers

const baseHandlers = {
    get,
    set

}

const get = (isReadOnly = false,shallow = false) => { // 这里也是函数柯里化的一种形式,返回的函数里面通过闭包的方法,能够拿到isReadOnly, 和 shallow 两个变量, 调用 reactive 时,两个都为false
        return function createGetter(target, key, receiver) {
          // ... 省略一部分处理当reactive传入一个reactiveObject的过程(ps:传入一个reactiveObject,会直接返回)
        
        const res = Reflect.get(target, key, receiver) // 拿到对应的value
        
         if (!isReadonly) {
          track(target, TrackOpTypes.GET, key) // ! 收集依赖,外部调用 target.xx,这里就会收集依赖。比如当你在watchEffect里面访问 target,xxx, 这个就会收集watchEffect的依赖(watchEffect传入的回调函数)
        }
        
         if (shallow) { // ! shallow 不会把子元素变为 reactive,对于一些第三方库提供的对象,不要变为reactive
              return res
        }
        
         if (isObject(res)) { // res 是一个对象的话,vue会把这个子对象再次转为reactive的
      // ! 并没有直接帮你把子属性全部转为reactive,而是在你需要调用的时候去转,vue2.0是直接递归所有的data,这里更好
              return isReadonly ? readonly(res) : reactive(res) // 将拿到的对象再次转为一个reactive的对象,这样 target.a.b 也是一个响应式的对象了
           }
        
      return res
        
     }
        
        
}
const set = (shallow = false) => { // 修改 target.xxx 时,就会触发这个set
    return function createSetter() {
        const oldValue = (target as any)[key]
        if (!shallow) { // 拿到的对象也是一个reactive的对象,要转为原始对象
          value = toRaw(value)
          if (!isArray(target) && isRef(oldValue) && !isRef(value)) { // 老的值是一个ref,但是新的值不是一个ref,vue不会把老的ref干掉,而是把老的ref.value的值替换掉
            oldValue.value = value
            return true
          }
        } else {
          // in shallow mode, objects are set as-is regardless of reactive or not
        }
         const result = Reflect.set(target, key, value, receiver) // 设置值
        if (target === toRaw(receiver)) {
          if (!hadKey) {trigger
            trigger(target, TriggerOpTypes.ADD, key, value)
          } else if (hasChanged(value, oldValue)) {
            trigger(target, TriggerOpTypes.SET, key, value, oldValue) // 通知依赖进行更新!访问值得时候触发了track,这里就是触发track收集的依赖。(触发watchEffect的回调函数)
          }
        }
        return result
    
    }


}







复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享