[Vue3.0源码分析]reactive

demongao
2408
2020-10-27

Target类型及属性

// 定义target属性
interface Target {
  // 标记为原始数据,则不能进行监控,只能为原始数据
  [ReactiveFlags.SKIP]?: boolean
  // 是否是可读可写响应式
  [ReactiveFlags.IS_REACTIVE]?: boolean
  // 是否为只读响应式
  [ReactiveFlags.IS_READONLY]?: boolean
  // 指向原始数据
  [ReactiveFlags.RAW]?: any
}
// 判断是否为可读可写响应式
export function isReactive(value: unknown): boolean {
  if (isReadonly(value)) {
      // 如果是可读的的,以原始数据进行判断
    return isReactive((value as Target)[ReactiveFlags.RAW])
  }
  return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}
// 判断是否为只读响应式
export function isReadonly(value: unknown): boolean {
  return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}
// 判断是否为代理数据
export function isProxy(value: unknown): boolean {
  return isReactive(value) || isReadonly(value)
}
// 通过RAW属性进行获取原始数据,如果原始数据不存在直接返回监控数据
export function toRaw<T>(observed: T): T {
  return (
    (observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
  )
}

reactive源码

export function reactive(target: object) {
  // 如果对象为Readonly,则不能创建Reactive,直接返回
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers
  )
}
  • Reactive: 可读可写的响应式数据结构,调用createReactiveObject, 传入第二个参数为false
  • Readonly: 只读的响应式数据结构, 调用createReactiveObject, 传入第二个参数为true
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<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
  // target[ReactiveFlags.RAW]指target为响应式
  // 如果是响应式则直接返回
  // 但是如果是创建Readonly类型数据,但是target是Reactive则需要重新创建
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  const proxyMap = isReadonly ? readonlyMap : reactiveMap
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // 
  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
}