vue3.2 reactivity 之 (ref && reactive) API源码解析

vue3.2 reactivity 之 ref API , reactive API源码解析

  • -------------- ref API -------------
  • ref
  • trackRefValue
  • triggerRefValue
  • shallowRef
  • isRef
  • toRef
  • toRefs
  • unref
  • proxyRefs
  • customRef
  • triggerRef
  • -------------- reactive API -------------
  • reactive
  • createReactiveObject
  • getTargetType
  • targetTypeMap
  • readonly
  • toReactive
  • isReactive
  • isReadonly
  • isProxy
  • shallowReactive
  • shallowReadonly
  • markRaw
  • toRaw

在代码块中我会添加注释,方便大家理解,配合vue文档看效果更佳
本篇针对 /packages/reactivity/src/ref.ts 与 /packages/reactivity/src/reactive.ts


-------------- ref API -------------


ref

export function ref<T extends object>(
 value: T
): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
// value 参数即为我们传进来的值
export function ref(value?: unknown) {
 return createRef(value, false) //调用createRef,并将参数传递进去
}

首先我们来看ref 的函数定义,上面的都是进行函数重载,主要看最下面的。
函数内部仅调用了createRef(value,false)

// 接收参数 rawValue, shallow, shallow即是否创建shallowRef
function createRef(rawValue: unknown, shallow: boolean) {
  //判断rawValue是否为ref,是则直接返回
  if (isRef(rawValue)) {
    return rawValue
  }
  //创建RefImpl类实例,并返回
  return new RefImpl(rawValue, shallow)
}

这里的逻辑很简单,判断是否为ref 是则返回,否则创建RefImpl 实例并返回

// RefImpl 类
class RefImpl<T> {
  private _value: T	//值(私有)
  private _rawValue: T	//原始值(私有)
  public dep?: Dep = undefined //副作用函数
  public readonly __v_isRef = true //标记是ref
  
  //构造方法,接收泛型value,以及boolean类型只读_shallow
  constructor(value: T, public readonly _shallow: boolean) {
    //_shallow为true,则直接把 value 赋值给 _rawValue 与 _value, 否则进行toRaw,toReactive转换
    this._rawValue = _shallow ? value : toRaw(value) 
    this._value = _shallow ? value : toReactive(value) 
  }

  get value() {
    trackRefValue(this) //收集依赖(订阅)
    return this._value
  }

  set value(newVal) {
    newVal = this._shallow ? newVal : toRaw(newVal) 
    // hasChanged 判断是否变更,来源于shared工具函数,内部调用 Object.is进行对比
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = this._shallow ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal) //触发依赖(发布)
    }
  }
}

RefImpl类中,constructor_value_rawValue进行赋值,get value() 收集依赖,set value() 触发依赖
当开发时,state.value 返回的则是值 _value, 如果ref() 传进一个对象在toReactive时会进行reactive() 转换,此时 _value 为 Proxy 响应式对象。
顺便贴下 hasCHanged实现,来源shared工具函数。

export const hasChanged = (value: any, oldValue: any): boolean =>
  !Object.is(value, oldValue)

trackRefValue

export function trackRefValue(ref: RefBase<any>) {
  // isTracking() 判断是否可进行依赖收集,此方法在 effect.ts 中,后续相关文章会介绍
  if (isTracking()) {
    ref = toRaw(ref)	//获取原始值
    // dep不存在则创建( 刚创建的RefImpl的dep值为undefined )
    if (!ref.dep) {
      //创建dep,此方法在 dep.ts 中后续相关文章会介绍
      // 简单介绍一下,dep就是一个 new Set()用于收集依赖(副作用), 
      // 此外还有dep.w,dep.n 用于对依赖收集的重复与嵌套进行优化处理
      ref.dep = createDep() 
    }
    // trackEffects 真正的收集依赖方法,此方法在 effect.ts 中,后续相关文章会介绍
    if (__DEV__) {
     //dev环境中传入一些便于调试的参数
      trackEffects(ref.dep, {
        target: ref,
        type: TrackOpTypes.GET,
        key: 'value'
      })
    } else {
      trackEffects(ref.dep)
    }
  }
}

可以看到trackRefValue 主要作用就是对RefImpl.dep进行初始化赋值。
因为只有当 RefImpl.get() 触发时(例: state.value),才会 trackRefValue 来对dep进行初始化(new Set()),所以当 **RefImpl.get()**没有被触发(例: 从未使用到state.value),则不进行dep初始化,优化节省空间。


triggerRefValue

export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
  ref = toRaw(ref) //获取原始值
  if (ref.dep) {
    // triggerEffects 真正的触发依赖方法,此方法在 effect.ts 中,后续相关文章会介绍
    if (__DEV__) {
      //dev环境中传入一些便于调试的参数
      triggerEffects(ref.dep, {
        target: ref,
        type: TriggerOpTypes.SET,
        key: 'value',
        newValue: newVal
      })
    } else {
      triggerEffects(ref.dep)
    }
  }
}

很简单,判断dep是否存在,是则触发依赖


shallowRef

export function shallowRef<T extends object>(
  value: T
): T extends Ref ? T : ShallowRef<T>
export function shallowRef<T>(value: T): ShallowRef<T>
export function shallowRef<T = any>(): ShallowRef<T | undefined>
export function shallowRef(value?: unknown) {
  return createRef(value, true)
}

ref区别就在于 createRef_shallow 为 true,表式创建浅层ref,即不走toRawtoReactive, 所以当传入值为对象时,_value 为普通对象,所以当修改对象某个属性时,不会触发副作用,例如修改state.value.age 不会触发副作用,只有修改state.value 时才会触发副作用


isRef

//判断是否为ref,如果是RefImpl实例则__v_isRef为true
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
export function isRef(r: any): r is Ref {
  return Boolean(r && r.__v_isRef === true)
}


toRef

export function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K
): ToRef<T[K]>

export function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K,
  defaultValue: T[K]
): ToRef<Exclude<T[K], undefined>>

export function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K,
  defaultValue?: T[K]
): ToRef<T[K]> {
  const val = object[key] 
  return isRef(val)
    ? val
    : (new ObjectRefImpl(object, key, defaultValue) as any)
}

object去除key对应value,如果是ref类型则直接返回,否则调用new ObjectRefImpl创建实例并返回, 我们来看一下ObjectRefImpl

class ObjectRefImpl<T extends object, K extends keyof T> {
  public readonly __v_isRef = true

  constructor(
    private readonly _object: T,
    private readonly _key: K,
    private readonly _defaultValue?: T[K]
  ) {}

  get value() {
    const val = this._object[this._key]
    return val === undefined ? (this._defaultValue as T[K]) : val
  }

  set value(newVal) {
    this._object[this._key] = newVal
  }
}

构造函数进行初始赋值,get value() 则返回 _object 对应值,当 _object 为响应式对象时如reactive 则收集依赖,如果为undefined则返回创建实例时传入的默认值, set value() 时,如果 _object 为响应式对象时,则触发依赖。


toRefs

export function toRefs<T extends object>(object: T): ToRefs<T> {
	//dev环境中不允许传进来非isProxy对象
  if (__DEV__ && !isProxy(object)) {
    console.warn(`toRefs() expects a reactive object but received a plain one.`)
  }
  //对 array类型做处理
  const ret: any = isArray(object) ? new Array(object.length) : {}
  //遍历object对每个值进行toRef转换
  for (const key in object) {
    ret[key] = toRef(object, key)
  }
  return ret
}

可以看出,toRefs就是把oject响应式对象中的每个属性对应的值进行toRef转换,并返回ret结果对象


unref

export function unref<T>(ref: T | Ref<T>): T {
  return isRef(ref) ? (ref.value as any) : ref
}

如果是ref则返回对应值value,否则返回本身


proxyRefs

export function proxyRefs<T extends object>(
  objectWithRefs: T
): ShallowUnwrapRef<T> {
  return isReactive(objectWithRefs)
    ? objectWithRefs
    : new Proxy(objectWithRefs, shallowUnwrapHandlers)
}

判断是否为 reactive, 是则返回,否则创建Proxy对象并返回,我们来看一下shallowUnwrapHandlers

const shallowUnwrapHandlers: ProxyHandler<any> = {
  //get时调用unref返回 ref.value实际值
  get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
  set: (target, key, value, receiver) => {
    const oldValue = target[key]
    if (isRef(oldValue) && !isRef(value)) {
      //当旧值为ref类型且新值不为ref类型时,对oldValue.value 进行赋值
      oldValue.value = value
      return true
    } else {
      //否则正常返回
      return Reflect.set(target, key, value, receiver)
    }
  }
}

当执行oldValue.value = value, get的时候则直接拿到oldValue.value


customRef

export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
  return new CustomRefImpl(factory) as any
}

创建CustomRefImpl实例并返回,有必要放一下factory的类型定义。

export type CustomRefFactory<T> = (
  track: () => void,
  trigger: () => void
) => {
  get: () => T
  set: (value: T) => void
}

接收 track(收集依赖),与trigger(触发依赖),返回带有get函数和set函数的对象
我们接着看 CustomRefImpl 类。

class CustomRefImpl<T> {
  public dep?: Dep = undefined

  private readonly _get: ReturnType<CustomRefFactory<T>>['get']
  private readonly _set: ReturnType<CustomRefFactory<T>>['set']

  public readonly __v_isRef = true

  constructor(factory: CustomRefFactory<T>) {
    //执行factory函数是,传入 trackRefValue, triggerRefValue
    // 这里用 ()=>trackRefValue(this),是为了解决用户在调用时的this指向问题
    const { get, set } = factory(
      () => trackRefValue(this),
      () => triggerRefValue(this)
    )
    this._get = get
    this._set = set
  }

  get value() {
    return this._get()
  }

  set value(newVal) {
    this._set(newVal)
  }
}

RefImpl实现其实差不多,同样是使用 trackRefValuetriggerRefValue 去做依赖收集触发,只不过get()set() 方法可由用户自定义,可以控制值得操作以及什么时候去做依赖的收集与触发。使用时,customRef(fn) 这里的fn就是factory(工厂), 由类型定义可知必须实现get()set(value) 方法, 并且接收 track(trackRefValue依赖收集) 与 trigger(triggerRefValue触发依赖)。


triggerRef

//手动触发 ref 对应依赖(副作用)
export function triggerRef(ref: Ref) {
  triggerRefValue(ref, __DEV__ ? ref.value : void 0)
}

传进ref,触发对应依赖,在vue文档中搭配shallowRef使用。


-------------- reactive API -------------

一些定义,函数中会用到,自己看哈哈哈


export const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  RAW = '__v_raw'
}

export interface Target {
  [ReactiveFlags.SKIP]?: boolean
  [ReactiveFlags.IS_REACTIVE]?: boolean
  [ReactiveFlags.IS_READONLY]?: boolean
  [ReactiveFlags.RAW]?: any
}

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>()

const enum TargetType {
  INVALID = 0,
  COMMON = 1,
  COLLECTION = 2
}

reactive

export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
  // 如果为只读代理,则直接返回, ReactiveFlags.IS_READONLY 值为 '__v_isReadonly'
  // 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,	// ProxyHandler 的一种,专门用来处理 reactive, 来源 baseHandlers
    mutableCollectionHandlers, // ProxyHandler 的一种,专门用来处理 reactive,来源 collectionHandlers
    reactiveMap		// new WeakMap()
  )
}

如果已为只读代理对象,则直接返回,否则调用 createReactiveObject


createReactiveObject

// target 参数 为传进来的值
// isReadonly 是否只读
// baseHandlers 基本处理函数(target为 Object, Array时使用)
// collectionHandlers 集合处理函数(target为 Map,WeakMap, Set, WeakSet 时使用)
// proxyMap 用来存储target对应的proxy,做缓存效果
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是响应式对象 && !(创建只读 &&  target是 reactive) 
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // 尝试从代理集合中获取target对应的代理对象proxy,如果存在则直接返回
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // 获取target类型,只有在白名单中的类型才可进行proxy代理,否则直接返回
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  //创建proxy实例
  const proxy = new Proxy(
    target,
    //类型是集合类的,则采用 collectionHandlers, 相反采用 baseHandlers
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  //向proxyMap中存储target对应的proxy对象
  proxyMap.set(target, proxy)
  return proxy
}

这里我把 白名单 列举出来 (Object,Array,Map,Set,WeakMap,WeakSet)
可以看到 createReactiveObject 中对各种边界进行处理,以及对target 转换后的 proxy 进行了缓存处理,重复reactvice(target) 时, 可以减少不必要的操作。


getTargetType

function getTargetType(value: Target) {
  //tip: Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
  return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
    ? TargetType.INVALID // 0
    : targetTypeMap(toRawType(value))	//toRawType 来源shared工具函数
}

value.__v_skip 为true 或 不可扩展,则返回 TargetType.INVALID , 代表无效
否则调用 targetTypeMap 获取对应type
顺便贴下 toRawType

//获取数据类型, toTypeString 中实际调用的是,Object.prototype.toString.call()
export const toRawType = (value: unknown): string => {
  return toTypeString(value).slice(8, -1)
}

targetTypeMap

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
  }
}

很简单,传入类型,判断获取TargetType对应的枚举值


readonly

export function readonly<T extends object>(
  target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
  return createReactiveObject(
    target,
    true,	//标记只读
    readonlyHandlers, // ProxyHandler 的一种,专门用来处理 readonly, 来源 baseHandlers
    readonlyCollectionHandlers, //ProxyHandler 的一种,专门用来处理 readonly, 来源 collectionHandlers
    readonlyMap
  )
}

toReactive

export const toReactive = <T extends unknown>(value: T): T =>
  isObject(value) ? reactive(value) : value

逻辑很简单,对传进来的value 进行判断,如果是对象类型则进行reactive转换,否则直接返回
顺便贴一下isObject实现,来源shared工具函数

export const isObject = (val: unknown): val is Record<any, any> =>
  val !== null && typeof val === 'object'

isReactive

//判断是否为reactive
export function isReactive(value: unknown): boolean {
  //判断是否为 readonly
  if (isReadonly(value)) {
  	// ReactiveFlags.RAW = '__v_raw'
    return isReactive((value as Target)[ReactiveFlags.RAW])
  }
  // ReactiveFlags.IS_REACTIVE= '__v_isReactive'
  return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}

如果为readonly,则重调 isReactive, 应对使用该api时,从响应式对象中创建只读proxy
const state = reactive({ name: ‘John’ })
const stateCopy = readonly(state)
如果是响应式对象,value.__v_isReactive 会返回创建get方法时(调用createGetter),传入的isReadonly参数, 取反返回,对应 shallowReactiveHandlers,mutableHandlers 则传入为 false或不传。


isReadonly

export function isReadonly(value: unknown): boolean {
// ReactiveFlags.IS_READONLY = __v_isReadonly
  return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}

如果是响应式对象,value.__v_isReadonly 会返回创建get方法时(调用createGetter),传入的isReadonly参数,对应 readonlyHandlers ,shallowReadonlyHandlers 则传入为 true。


isProxy

export function isProxy(value: unknown): boolean {
  return isReactive(value) || isReadonly(value)
}

判断是否为 reactivereadonly


shallowReactive

export function shallowReactive<T extends object>(
  target: T
): ShallowReactive<T> {
  return createReactiveObject(
    target,
    false,
    shallowReactiveHandlers,  // ProxyHandler 的一种,专门用来处理 shallowReactive, 来源 baseHandlers
    shallowCollectionHandlers,  // ProxyHandler 的一种,专门用来处理 shallowReactive, 来源 collectionHandlers
    shallowReactiveMap
  )
}

reactive 区别在于使用了 shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap


shallowReadonly

export function shallowReadonly<T extends object>(
  target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
  return createReactiveObject(
    target,
    true,
    shallowReadonlyHandlers, // ProxyHandler 的一种,专门用来处理 shallowReadonly, 来源 baseHandlers
    shallowReadonlyCollectionHandlers, // ProxyHandler 的一种,专门用来处理 shallowReadonly, 来源 collectionHandlers
    shallowReadonlyMap
  )
}

readonly 区别在于使用了 shallowReadonlyHandlers, shallowReadonlyCollectionHandlers, shallowReadonlyMap


markRaw

//标记一个对象,使其永远不会转换为 proxy。返回对象本身。
export function markRaw<T extends object>(value: T): T {
	// def 来源于 shared工具函数
	// ReactiveFlags.SKIP = '__v_skip'
  def(value, ReactiveFlags.SKIP, true)
  return value
}

通过defvalue定义一个 __v_skip 不可写并为true的值
这个 __v_skip 会在 createReactiveObjectgetTargetType 中 被使用作为判断
顺便贴下def实现

export const def = (obj: object, key: string | symbol, value: any) => {
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: false,
    value
  })
}

toRaw

//获取原始值
export function toRaw<T>(observed: T): T {
  // ReactiveFlags 是一个枚举类型 RAW 值为 '__v_raw'
  const raw = observed && (observed as Target)[ReactiveFlags.RAW]
  return raw ? toRaw(raw) : observed
}

可以看到,如果 observed.__v_raw取值为false,toRaw则返回observed
这里用 __v_raw 获取是因为在响应式proxy对象的get方法中,对获取该key做了处理来返回原始值target。在这里放个截图
vue3.2 reactivity 之 (ref && reactive) API源码解析_第1张图片


这篇其实更像是对api的实现进行解析,如果有写的不好的地方欢迎指正,谢谢大家。

你可能感兴趣的:(前端技术,javascript,vue.js,前端)