【源码阅读】vue3 - reactive 源码探究二

902 阅读6分钟

mutableHandlers 执行过程探究

baseHandlers 是在源码的 packages --> reactivity --> src --> baseHandlers .ts

baseHandlers 是当代理对象为 Object(普通的对象) 和 Array 的 handler 即 new Proxy(Target,badeHandlers),baseHandlers 处理器传入的值为 mutableHandlers 对象包含了 get,set,deleteProperty,has,ownKeys 5个方法,对了 读,写,删除,in ,for in 这5个操作。

export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}
​
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (isReadonly(target)) {return target}
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}

下面我们分别看下这 5 个方法

get/has/ownKeys

get 函数是执行 createGetter 方法后得到的, createGetter 接收两个参数默认为 false,分别控制是否为浅响应(shallowGet:只对对象最外层做响应式处理),只读(readonlyGet:数据只能读不能写),浅只读(shallowReadonlyGet:只限制最外层不能改,并且只对外层做响应式处理)。get 函数主要做的以下5件事情:

  • 对特殊的key值的处理
  • 被代理对象为数组的处理
  • 深响应,浅响应,只读,浅只读的处理
  • 读取值,并且实现自动脱ref
  • track 函数执行,进行依赖收集

细节部分如下:

const get = /*#__PURE__*/ createGetter()
const shallowGet = /*#__PURE__*/ createGetter(false, true)
const readonlyGet = /*#__PURE__*/ createGetter(true)
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
​
export const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  IS_SHALLOW = '__v_isShallow',
  RAW = '__v_raw'
}
​
function createGetter(isReadonly = false, shallow = false) {
  return function get(target: Target, key: string | symbol, receiver: object) {
    //特殊key值的处理
    if (key === ReactiveFlags.IS_REACTIVE) { // key === '__v_isReactive',如果不是只读,则是响应式
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) { // key === '__v_isReadonly',根据传入的 isReadonly 参数决定
      return isReadonly
    } else if (key === ReactiveFlags.IS_SHALLOW) { // key === '__v_isShallow',根据传入的 shallow 参数决定
      return shallow
    } else if (  // key === '__v_raw',并且已经记录在 WeakMap 对象内,返回被代理对象,用于 toRaw 方法
      key === ReactiveFlags.RAW &&
      receiver ===
        (isReadonly
          ? shallow
            ? shallowReadonlyMap
            : readonlyMap
          : shallow
          ? shallowReactiveMap
          : reactiveMap
        ).get(target)
    ) {
      return target
    }
    
    // 数组的处理,针对数组查找方法 'includes', 'indexOf', 'lastIndexOf',
    // 影响数组长度方法 'push', 'pop', 'shift', 'unshift', 'splice' 做特殊的处理  
    /**
        const arrayInstrumentations = createArrayInstrumentations()
        function createArrayInstrumentations() {
            (['includes', 'indexOf', 'lastIndexOf'] as const).forEach( ... )
            (['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach( ... )
        }
    */  
    const targetIsArray = isArray(target)
​
    if (!isReadonly) {
      if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
        return Reflect.get(arrayInstrumentations, key, receiver)
      }
      if (key === 'hasOwnProperty') {
        return hasOwnProperty
      }
    }
​
    // 读取属性值,直接返回结果 
    const res = Reflect.get(target, key, receiver)
​
    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res
    }
​
    //  track 函数执行,进行依赖收集 
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }
    
    // 浅响应直接返回结果  
    if (shallow) {
      return res
    }
​
    // 自动脱 ref :读取的值是 ref ,返回它的 value 属性值
    if (isRef(res)) {
      // ref unwrapping - skip unwrap for Array + integer key.
      return targetIsArray && isIntegerKey(key) ? res : res.value
    }
​
    // 读取的属性值是对象,并且是只读,递归调用 readonly,非只读递归调用 reactive  
    if (isObject(res)) {
      // Convert returned value into a proxy as well. we do the isObject check
      // here to avoid invalid value warning. Also need to lazy access readonly
      // and reactive here to avoid circular dependency.
      return isReadonly ? readonly(res) : reactive(res)
    }
​
    return res
  }
}

has 和 ownKeys 这两个方法相对简单,仅仅是拦截操作调用 track 进行依赖收集

function has(target: object, key: string | symbol): boolean {
  const result = Reflect.has(target, key)
  if (!isSymbol(key) || !builtInSymbols.has(key)) {
    track(target, TrackOpTypes.HAS, key)
  }
  return result
}
​
function ownKeys(target: object): (string | symbol)[] {
  track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
  return Reflect.ownKeys(target)
}

set/deleteProperty

set 函数是执行 createSetter 方法后得到的, createSetter 接收一个参数默认 false,区分是否为浅响应。set 函数主要做的以下3件事情:

  • 区分操作是新增还是修改
  • 设置值
  • 调用 trigger 函数派发更新
const set = /*#__PURE__*/ createSetter()
const shallowSet = /*#__PURE__*/ createSetter(true)
​
function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    let oldValue = (target as any)[key]
    if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
      return false
    }
    if (!shallow) {
      if (!isShallow(value) && !isReadonly(value)) {
        oldValue = toRaw(oldValue)
        value = toRaw(value)
      }
      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)
    // don't trigger if target is something up in the prototype chain of original
    // target 和 被代理对象判断是为了解决 key 值是在原型链上的属性,set 时会触发两次 trigger 的问题
    if (target === toRaw(receiver)) {
      if (!hadKey) {
        trigger(target, TriggerOpTypes.ADD, key, value)
      } else if (hasChanged(value, oldValue)) {
        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
      }
    }
    return result
  }
}

deleteProperty 也相对简单,拦截删除操作调用 trigger 函数派发更新

function deleteProperty(target: object, key: string | symbol): boolean {
  const hadKey = hasOwn(target, key)
  const oldValue = (target as any)[key]
  const result = Reflect.deleteProperty(target, key)
  if (result && hadKey) {
    trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
  }
  return result
}

mutableCollectionHandlers 执行过程探究

mutableCollectionHandlers 是在源码的 packages --> reactivity --> src --> collectionHandlers .ts

mutableCollectionHandlers 是当代理对象为 Map,Set,WeakMap,WeakSet 的 handler 即 new Proxy(Target,mutableCollectionHandlers ),mutableCollectionHandlers 仅有 get 操作。

export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
  get: /*#__PURE__*/ createInstrumentationGetter(false, false)
}
​
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (isReadonly(target)) {return target}
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}

Map,Set,WeakMap,WeakSet 为什么只需要拦截 get 操作呢,这是因为这些集合类对象的操作,都是通过调用对象上的方法和属性来完成的。通过拦截 get 操作,key 值传递的是方法名, 重写方法即可。

对于 get 操作的拦截还有一点需要注意:

new Proxy(target, 
          {
                get(target, key, recevier) {
                    // 不能直接返回 Reflect.get ,否则会一直递归导致栈溢出
                    // return Reflect.get(target, key, recevier)
                    return () => {
                        // 返回的其实需要执行的方法而不是属性
                        return Reflect.get(target, key, recevier)
                    }
                }
            })

接下来看下 createInstrumentationGetter 函数的执行,该函数接收两个参数默认都为 false,用来控制是否为浅响应还是只读。主要完成2件事件:

  • 对特殊的key值的处理
  • 根据传入的参数选择合适的 instrumentations
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
  get: /*#__PURE__*/ createInstrumentationGetter(false, false)
}
​
export const shallowCollectionHandlers: ProxyHandler<CollectionTypes> = {
  get: /*#__PURE__*/ createInstrumentationGetter(false, true)
}
​
export const readonlyCollectionHandlers: ProxyHandler<CollectionTypes> = {
  get: /*#__PURE__*/ createInstrumentationGetter(true, false)
}
​
export const shallowReadonlyCollectionHandlers: ProxyHandler<CollectionTypes> = {
  get: /*#__PURE__*/ createInstrumentationGetter(true, true)
}
function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
  const instrumentations = shallow
    ? isReadonly
      ? shallowReadonlyInstrumentations
      : shallowInstrumentations
    : isReadonly
    ? readonlyInstrumentations
    : mutableInstrumentations
​
  return (
    target: CollectionTypes,
    key: string | symbol,
    receiver: CollectionTypes
  ) => {
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    } else if (key === ReactiveFlags.RAW) {
      return target
    }
​
    return Reflect.get(
      hasOwn(instrumentations, key) && key in target
        ? instrumentations
        : target,
      key,
      receiver
    )
  }
}

instrumentations 又是调用 createInstrumentations 函数生成的,可以看出对于集合对象的 get,has,add,set,delete,clear,forEach 方法,size 属性均进行了重写

const [
  mutableInstrumentations,
  readonlyInstrumentations,
  shallowInstrumentations,
  shallowReadonlyInstrumentations
] = /* #__PURE__*/ createInstrumentations()
​
function createInstrumentations() {
  const mutableInstrumentations: Record<string, Function | number> = {
    get(this: MapTypes, key: unknown) {
      return get(this, key)
    },
    get size() {
      return size(this as unknown as IterableCollections)
    },
    has,
    add,
    set,
    delete: deleteEntry,
    clear,
    forEach: createForEach(false, false)
  }
​
  const shallowInstrumentations: Record<string, Function | number> = {
    get(this: MapTypes, key: unknown) {
      return get(this, key, false, true)
    },
    get size() {
      return size(this as unknown as IterableCollections)
    },
    has,
    add,
    set,
    delete: deleteEntry,
    clear,
    forEach: createForEach(false, true)
  }
​
  const readonlyInstrumentations: Record<string, Function | number> = {
    get(this: MapTypes, key: unknown) {
      return get(this, key, true)
    },
    get size() {
      return size(this as unknown as IterableCollections, true)
    },
    has(this: MapTypes, key: unknown) {
      return has.call(this, key, true)
    },
    add: createReadonlyMethod(TriggerOpTypes.ADD),
    set: createReadonlyMethod(TriggerOpTypes.SET),
    delete: createReadonlyMethod(TriggerOpTypes.DELETE),
    clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
    forEach: createForEach(true, false)
  }
​
  const shallowReadonlyInstrumentations: Record<string, Function | number> = {
    get(this: MapTypes, key: unknown) {
      return get(this, key, true, true)
    },
    get size() {
      return size(this as unknown as IterableCollections, true)
    },
    has(this: MapTypes, key: unknown) {
      return has.call(this, key, true)
    },
    add: createReadonlyMethod(TriggerOpTypes.ADD),
    set: createReadonlyMethod(TriggerOpTypes.SET),
    delete: createReadonlyMethod(TriggerOpTypes.DELETE),
    clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
    forEach: createForEach(true, true)
  }
​
  const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
  iteratorMethods.forEach( ... )
​
  return [
    mutableInstrumentations,
    readonlyInstrumentations,
    shallowInstrumentations,
    shallowReadonlyInstrumentations
  ]
}

get

get 函数接收 4 个参数,isReadonly,isShallow 默认为 false,主要功能如下:

  • track 函数执行,进行依赖收集
  • 根据 isReadonly,isShallow 是否需要深响应和只读
function get(
  target: MapTypes,
  key: unknown,
  isReadonly = false,
  isShallow = false
) {
  // #1772: readonly(reactive(Map)) should return readonly + reactive version
  // of the value
  target = (target as any)[ReactiveFlags.RAW]
  const rawTarget = toRaw(target)
  const rawKey = toRaw(key)
  if (!isReadonly) {
    if (key !== rawKey) {
      track(rawTarget, TrackOpTypes.GET, key)
    }
    track(rawTarget, TrackOpTypes.GET, rawKey)
  }
  const { has } = getProto(rawTarget)
  // wrap 是根据 isReadonly,isShallow 选择的函数,其值如下:
  /**
    const toShallow = <T extends unknown>(value: T): T => value
    export const toReadonly = <T extends unknown>(value: T): T =>
        isObject(value) ? readonly(value as Record<any, any>) : value
    export const toReactive = <T extends unknown>(value: T): T =>
        isObject(value) ? reactive(value) : value   
  */
  const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
  if (has.call(rawTarget, key)) {
    return wrap(target.get(key))
  } else if (has.call(rawTarget, rawKey)) {
    return wrap(target.get(rawKey))
  } else if (target !== rawTarget) {
    // #3602 readonly(reactive(Map))
    // ensure that the nested reactive `Map` can do tracking for itself
    target.get(key)
  }
}

set

set 函数作用和主要做的以下2件事情:

  • 区分操作是新增还是修改
  • 调用 trigger 函数派发更新
function set(this: MapTypes, key: unknown, value: unknown) {
  value = toRaw(value)
  const target = toRaw(this)
  const { has, get } = getProto(target)
​
  let hadKey = has.call(target, key)
  if (!hadKey) {
    key = toRaw(key)
    hadKey = has.call(target, key)
  } else if (__DEV__) {
    checkIdentityKeys(target, has, key)
  }
​
  const oldValue = get.call(target, key)
  target.set(key, value)
  if (!hadKey) {
    trigger(target, TriggerOpTypes.ADD, key, value)
  } else if (hasChanged(value, oldValue)) {
    trigger(target, TriggerOpTypes.SET, key, value, oldValue)
  }
  return this
}

最后总结下 handler,当 target 是 Object 和 Array 时,handler 为 mutableHandlers,会劫持 get,set,has,deleteProperty,ownKeys,对属性进行依赖收集和派发更新;当 target 是 Map,Set,WeakMap,WeakSet时,handler 为 mutableCollectionHandlers,只拦截 get,对方法进行依赖收集和派发更新