Vue3源码解析--reactive篇

在之前的一篇文章Vue3源码解析--ref篇,详细阅读分析了vue3响应式ref相关的源码实现,本编文章,带领大家一起学习vue3响应式API reactive源码的实现。

相比于vue2中数据响应式实现方式的不同,Vue3使用Proxy代替在vue2中使用的Object.defineProperty的数据代理拦截操作,在响应式数据代理处理的性能方面有很大的提升。

一起来看reactive相关的api具体源码实现:

1.reactive

官方描述:返回对象的响应式副本,响应式转换是“深层”的——它影响所有嵌套 property,返回的 proxy 是等于原始对象的。建议只使用响应式 proxy,避免依赖原始对象。

1. 1 源码定义
  • 文件定义:packages\reactivity\src\reactive.ts

在了解reactive源码实现之前,我们先看文件开头定义的一些有关reactive的类型和接口的定义,理解了这些类型的定义,更有助于接下来的源码分析阅读

// reactive相关标识
export const enum ReactiveFlags {
  SKIP = '__v_skip', // 跳过代理
  IS_REACTIVE = '__v_isReactive', // reactive代理对象标识
  IS_READONLY = '__v_isReadonly', // 只读代理标识
  RAW = '__v_raw' // 代理对象的原始值标识
}
// 代理对象接口描述
export interface Target {
  [ReactiveFlags.SKIP]?: boolean
  [ReactiveFlags.IS_REACTIVE]?: boolean
  [ReactiveFlags.IS_READONLY]?: boolean
  [ReactiveFlags.RAW]?: any
}
// 代理对象类型
const enum TargetType {
  INVALID = 0, // 无效的
  COMMON = 1, // 可以代理的object,array类型
  COLLECTION = 2 // 可以代理的集合类型,map set weakMap,weakSet
}
// 在此定义了4类reactive相关的map用来缓存代理对象
export const reactiveMap = new WeakMap<Target, any>()
export const shallowReactiveMap = new WeakMap<Target, any>()
export const readonlyMap = new WeakMap<Target, any>()
export const shallowReadonlyMap = new WeakMap<Target, any>()
// 工具函数,检测代理对象的类型,并区分Object,Array和Map,Set,WeakMap,WeakSet的类型
function targetTypeMap(rawType: string) {
  switch (rawType) {
    case 'Object':
    case 'Array':
      return TargetType.COMMON // 1
    case 'Map':
    case 'Set':
    case 'WeakMap':
    case 'WeakSet':
      return TargetType.COLLECTION // 2
    default:
      return TargetType.INVALID // 0
  }
}
// 工具函数-获取代理对象的类型
function getTargetType(value: Target) {
  return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
    ? TargetType.INVALID
    : targetTypeMap(toRawType(value))
}
复制代码
1. 2 源码实现
export function reactive(target: object) {
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}
复制代码

说明:

  • 首先判断传入参数target是否已经是只读代理对象,如果已经是只读代理对象,直接返回target本身
  • 调用createReactiveObject()方法进行代理操作

2. shallowReactive

官方描述:创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。

  • 描述解读:shallowReactive即为浅代理的意思,只会使得对象自身属性值为响应式数据,加入对象属性对应值为复杂类型数据,则不会递归处理使其复杂类型值也成为响应式数据,即只处理一层。
2.1 源码实现
export function shallowReactive<T extends object>(target: T): T {
  return createReactiveObject(
    target,
    false,
    shallowReactiveHandlers,
    shallowCollectionHandlers,
    shallowReactiveMap
  )
}
复制代码

说明:内部直接调用createReactiveObject()方法返回,关于createReactiveObject()方法,我们放在下面再具体深入分析

3. readonly

官方描述:接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。

3. 1 源码实现
export function readonly<T extends object>(
  target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
  return createReactiveObject(
    target,
    true,
    readonlyHandlers,
    readonlyCollectionHandlers,
    readonlyMap
  )
}
复制代码

4. shallowReadonly

官方描述:创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)

4.1 源码实现
export function shallowReadonly<T extends object>(
  target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
  return createReactiveObject(
    target,
    true,
    shallowReadonlyHandlers,
    shallowReadonlyCollectionHandlers,
    shallowReadonlyMap
  )
}
复制代码

5. isReadonly

官方描述:检查对象是否是由 readonly 创建的只读代理。

扫描二维码关注公众号,回复: 13428824 查看本文章
5.1 源码实现
export function isReadonly(value: unknown): boolean {
  return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}
复制代码

说明:将传入参数value类型推断为前面提到声明的Target类型,并检查是否存在__v_isReadonly属性标识

6. isReactive

官方描述:检查对象是否是由 reactive 创建的响应式代理

6.1 源码实现
export function isReactive(value: unknown): boolean {
  if (isReadonly(value)) {
    return isReactive((value as Target)[ReactiveFlags.RAW])
  }
  return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}
复制代码

说明:

  • 首先检查是否由readonly API创建,如果是由readonly api创建,检查readonly源对象是否为reactive
  • 否则,默认返回传入参数是否含有__v_isReactive标识

7. isProxy

官方描述:检查对象是否是由 reactive 或 readonly 创建的 proxy

7.1 源码定义
export function isProxy(value: unknown): boolean {
  return isReactive(value) || isReadonly(value)
}
复制代码

isProxy源码非常简单,即调用isReactive和isReadonly方法

8. toRaw

官方描述:返回 reactive 或 readonly 代理的原始对象

8.1 源码定义
export function toRaw<T>(observed: T): T {
  return (
    (observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
  )
}
复制代码

说明:

  • 返回传入参数的__v_raw属性值或者直接原值返回

9. markRaw

官方描述:标记一个对象,使其永远不会转换为 proxy。

9.1 源码定义
export function markRaw<T extends object>(value: T): T {
  def(value, ReactiveFlags.SKIP, true)
  return value
}

// def 为源码公共模块的一个工具方法
export const def = (obj: object, key: string | symbol, value: any) => {
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: false,
    value
  })
}
复制代码

说明:

  • markRaw内部调用def方法,传入接受参数value, __v_skip,true3个参数,并返回传入参数值
  • def方法通过Object.defineProperty修改传入对象的配置属性和可枚举属性

10.createReactiveObject

在前面我们看到,reactive,shallowReactive,readonly,shallowReadonly这4个API,其内部都是通过调用createReactiveObject方法实现,我们放在最后来看下这个方法的源码实现

10.1源码定义
function createReactiveObject(
  target: Target,
  isReadonly: boolean, // reactive false
  baseHandlers: ProxyHandler<any>, // reactive mutableHandlers
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any> // reactive reactiveMap
) {
  if (!isObject(target)) {
    // 如果不是对象,即不是Object,Array,Map,Set,WeakMap,WeakSet类型的,就不可以代理,直接返回
    if (__DEV__) {
      // 开发环境下给出警告
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // 如果target已经是个proxy对象,那么就直接返回该对象
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // 如果target已经存在对应的代理对象,则找到对应的代理对象并返回
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // 获取target的类型,如果其为INVALID,说明不能进行代理,所以直接返回target
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  // 根据target生成对应的proxy对象
  const proxy = new Proxy(
    target,
    // 区分 object array和map,set,weakMap,weakSet
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers 
  )
  // 将代理对象缓存在对应proxyMap中
  proxyMap.set(target, proxy)
  return proxy
}
复制代码

说明:

  • reactive,shallowReactive,readonly,shallowReadonly这4个API在调用createReactiveObject方法上,分别传入各自对应的new Proxy()构造函数get和set操作函数,分别执行各自的内部实现
  • createReactiveObject方法的最后一个参数proxyMap为reactive,shallowReactive,readonly,shallowReadonly这四个分别创建的weakMap用来缓存各自代理对象。

11. reactive Proxy get handler--mutableHandlers

我们接着深入看下reactive API最核心的实现之一Proxy get handlers读取操作函数

const get = /*#__PURE__*/ createGetter()
const set = /*#__PURE__*/ createSetter()
function createGetter(isReadonly = false, shallow = false) {
  return function get(target: Target, key: string | symbol, receiver: object) {
    // 访问 __v_isReactive
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      // 访问 __v_isReadonly
      return isReadonly
    } else if (
      // 如果访问的 key 是 __v_raw则直接返回 target
      key === ReactiveFlags.RAW &&
      receiver ===
        (isReadonly
          ? shallow
            ? shallowReadonlyMap
            : readonlyMap
          : shallow
            ? shallowReactiveMap
            : reactiveMap
        ).get(target)
    ) {
      return target
    }

    const targetIsArray = isArray(target)
    // 如果 target 是数组并且 key 属于八个方法之一 ['includes', 'indexOf', 'lastIndexOf','push', 'pop', 'shift', 'unshift', 'splice']
    if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
      return Reflect.get(arrayInstrumentations, key, receiver)
    }
    const res = Reflect.get(target, key, receiver)
    // 如果 key 是 symbol 并且属于 symbol 的内置方法之一,或者访问的是原型对象,直接返回结果,不收集依赖。
    if (
      isSymbol(key)
        ? builtInSymbols.has(key as symbol)
        : isNonTrackableKeys(key)
    ) {
      return res
    }
    // 如果不是只读对象,收集依赖
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }
    // 浅层响应立即返回,不递归调用 reactive()
    if (shallow) {
      return res
    }
    // 如果是 ref 对象,则返回真正的值,即 ref.value,数组除外。
    if (isRef(res)) {
      const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
      return shouldUnwrap ? res.value : res
    }
    // 由于 proxy 只能代理一层,所以 target[key] 的值如果是对象,就继续对其进行代理
    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }
}

function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    // 取旧值
    let oldValue = (target as any)[key]
    // 深度代理情况下,我们需要手动处理属性值为ref的情况,将trigger交给ref来触发
    if (!shallow) {
      value = toRaw(value)
      oldValue = toRaw(oldValue)
      if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
        oldValue.value = value
        return true
      }
    } else {
      // in shallow mode, objects are set as-is regardless of reactive or not
    }
    // 判断是新增或者修改
    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key)
    const result = Reflect.set(target, key, value, receiver)
    if (target === toRaw(receiver)) {
      if (!hadKey) {
        // 如果 target 没有 key,就代表是新增操作,需要触发依赖
        trigger(target, TriggerOpTypes.ADD, key, value)
      } else if (hasChanged(value, oldValue)) {
        // 更新--如果新旧值不相等,才触发依赖
        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
      }
    }
    return result
  }
}
export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}
复制代码

猜你喜欢

转载自juejin.im/post/7035254182906953759