vue3源码之旅-reactive

1,091 阅读2分钟

更多文章

前言

用了组合式api一段时间了,报着知其然、知其所以然的态度去阅读尤大大的代码,分享一下关于reactive部分,按照官网APIreactive部分逐个分析,为了方便理解可以参考简化后代码

简化代码

vue3-reactive源码位置

Proxy

Proxy相信大家都不陌生了,vue3使用Proxy替代了defineProperty,解决了defineProperty带来的弊端,而reactive部分的实现和Proxy息息相关,不了解Proxy请看阮一峰es6入门,接下来开始源码分析

reactive

看一下源码中是如何定义reactive

export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}
// createReactiveObject
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // target is already a Proxy, return it.
  // exception: calling readonly() on a reactive object
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // only a whitelist of value types can be observed.
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)
  return proxy
}

createReactiveObject方法即为我们创造一个reactive的方法,此部分代码和createReactiveObject简化一下如下:

function reactive(target) {
  return createReactiveObject(target, reactiveMap, mutableHandlers)
}
function createReactiveObject(target, proxyMap, baseHandlers) {
  // 非对象return
  if (!isObject(target)) {
    return target
  }
  // 是否已存在
  const existingProxy = proxyMap.get(target)
  // 存在返回
  if(existingProxy) return existingProxy
  // 判断类型
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) return target
  // 通过proxy生成新对象
  const proxy = new Proxy(target, baseHandlers)
  // 缓存数据
  proxyMap.set(target, proxy)

  return proxy
}

可以看到最终返回的是一个Proxy实例,在简化一下,reactive的实现如下:

function reactive(target) {
  return new Proxy(target, {
    get,
    set
  })
}

getset等的实现至关重要,源码中的实现还是比较复杂的,这里简化一下(尽量使用源码中的代码):

/**
* @param isReadOnly 是否只读
* @param shallow 是否为shallowReadonly、shallowReactive
*/
function createGetter(isReadOnly = false, shallow = false) {
  return function get(target, key, receiver) {
    // 判断是否为reactive
    if(key === IS_REACTIVE) return !isReadOnly
    // 判断是否为readonly
    else if(key === IS_READONLY) return isReadOnly
    // toRaw操作判断
    else if(
      key === RAW &&
      receiver === 
        (
          isReadOnly
            ? shallow
              ? shallowReadonlyMap
              : readonlyMap
            : shallow
              ? shallowReactiveMap
              : reactiveMap
        ).get(target)
    ) return target

    const result = Reflect.get(target, key, receiver)
    // shallow是否为true,若为true不在进行深度处理
    if (shallow) return result
    // 若为对象,深度处理
    if(isObject(result)) return isReadOnly ? readonly(result) : reactive(result)
    
    return result
  }
}

function createSetter() {
  return function set(target, key, value, receiver) {
    console.log(`${key}发生了改变`)
    const result = Reflect.set(target, key, value, receiver)
    return result
  }
}

const get = createGetter();
const set = createSetter();

此处可以看到rective对对象做了深度处理,简化一下get如下:

function createGetter(isReadOnly = false, shallow = false) {
  return function get(target, key, receiver) {
    const result = Reflect.get(target, key, receiver)
    // 若为对象,深度处理
    if(isObject(result)) return isReadOnly ? readonly(result) : reactive(result)
    
    return result
  }
}

readonly

当调用readonly方法时,getisReadOnly将设置为true,并且setdeleteProperty等方法都将重写,阻止并提示只读

const readonlyHandlers = {
  get: createGetter(true),
  set: (target, key) => {
    console.warn(
      `Set operation on key "${String(key)}" failed: target is readonly.`,
      target
    );
    return true
  },
  deleteProperty(target, key) {
    if (__DEV__) {
      console.warn(
        `Delete operation on key "${String(key)}" failed: target is readonly.`,
        target
      )
    }
    return true
  }
}
function readonly(target) {
  return createReactiveObject(target, readonlyMap, readonlyHandlers)
}

shallowReactive

当调用shallowReactive方法时,getshallow将设置为true,数据将不会进行深层次的处理

function shallowReactive(target) {
  return createReactiveObject(target, shallowReactiveMap, shallowReactiveHandlers)
}
const shallowReactiveHandlers = {
  get: createGetter(false, true),
  set
}

shallowReadonly

getisReadOnlyshallow入参同时设置为true

function shallowReadonly(target) {
  return createReactiveObject(target, shallowReadonlyMap, shallowReadonlyHandlers)
}
const shallowReadonlyHandlers = {
  get: createGetter(true, true),
  set(target, key) {
    // readonly 的响应式对象不可以修改值
    console.warn(
      `Set operation on key "${String(key)}" failed: target is readonly.`,
      target
    );
    return true;
  },
};

isReactive

当获取IS_REACTIVE属性时,get方法中会返回!isReadOnly,非只读即为reactive

function isReactive(value) {
  return !!value[IS_REACTIVE]
}

isReadonly

当获取IS_READONLY属性时,get方法中会返回isReadOnly

function isReadonly(value) {
  return !!value[IS_READONLY]
}

isProxy

isProxy即为isReactive或者isReadonly

function isProxy(value) {
  return isReactive(value) || isReadonly(value)
}

toRaw

toRaw的作用是获取原始对象,当我们调用toRaw方法时会去获取value[RAW]get方法中会判断该属性并获取相应的缓存数据,若是命中则返回原始数据(对应get中三目运算那部分)

function toRaw(value) {
  const raw = value[RAW]
  return raw ? toRaw(raw) : value
}

markRaw

markRaw为给数据加一个标识skipcreateReactiveObject中判断命中skip时会立即返回原始对象,永远都不会走向代理

function markRaw(value) {
  def(value, SKIP, true)
  return value
}

结语

配合简化后代码看着更香