亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Vue3?源碼分析reactive?readonly實例

 更新時間:2022年10月21日 16:55:21   作者:ChrisLey  
這篇文章主要為大家介紹了Vue3?源碼分析reactive?readonly實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

上次一起閱讀了watch和computed的源碼,其實應該先看副作用effect,因為各個響應式的API里基本都用到了,等結束了reactive和readonly和ref,就一起看看effect。這次要說的是reactive和readonly,兩者在實現上流程大體一致。尤其是對Map和Set的方法的代理攔截,多少有點妙。

一、reactive 和 readonly

Vue3使用Proxy來替代Vue2中Object.defineProperty。

const target = {
  name: 'onlyy~'
}
// 創(chuàng)建一個對target的代理
const proxy = new Proxy(target, {
  // ...各種handler,例如get,set...
  get(target, property, receiver){
    // 其它操作
    // ...
    return Reflect.get(target, property, receiver)
  }
})

1. reactive相關類型

reactive利用Proxy來定義一個響應式對象。

  • Target:目標對象,包含幾個標志,以及__v_raw字段,該字段表示它原本的非響應式狀態(tài)的值;
export interface Target {
  [ReactiveFlags.SKIP]?: boolean
  [ReactiveFlags.IS_REACTIVE]?: boolean
  [ReactiveFlags.IS_READONLY]?: boolean
  [ReactiveFlags.IS_SHALLOW]?: 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
}

2. 相關全局變量與方法

  • ReactiveFlags:定義了各種標志對應的字符串(作為reactive對象的屬性)的枚舉;
  • reactiveMap
  • shallowReactiveMap
  • readonlyMap
  • shallowReadonlyMap:這幾個Map分別用于存放對應API生成的響應式對象(以目標對象為key,代理對象為value),便于后續(xù)判斷某個對象是否存在已創(chuàng)建的響應式對象;
  • TargetType:枚舉成員的內容分別用于區(qū)分代理目標是否校驗合法、普通對象、Set或Map;
// 各個標志枚舉
export const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  IS_SHALLOW = '__v_isShallow',
  RAW = '__v_raw'
}
// ...
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
}

然后是兩個函數:targetTypeMap用于判斷各種JS類型屬于TargetType中的哪種;getTargetType用于獲取target對應的TargetType類型。

function targetTypeMap(rawType: string) {
  switch (rawType) {
    case 'Object':
    case 'Array':
      return TargetType.COMMON
    case 'Map':
    case 'Set':
    case 'WeakMap':
    case 'WeakSet':
      return TargetType.COLLECTION
    default:
      return TargetType.INVALID
  }
}
function getTargetType(value: Target) {
  return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
    ? TargetType.INVALID
    : targetTypeMap(toRawType(value))
}

3. reactive函數

reactive入參類型為object,返回值類型是UnwrapNestedRefs,對嵌套的Ref進行了解包。意味著即使reactive接收一個Ref,其返回值也不用再像Ref那樣通過.value來讀取值。源碼的注釋中也給出了示例。

/*
 * const count = ref(0)
 * const obj = reactive({
 *   count
 * })
 *
 * obj.count++
 * obj.count // -> 1
 * count.value // -> 1
 */

reactive內部調用createReactiveObject來創(chuàng)建響應式對象。瞄一眼入參有五個:

  • target:代理目標;
  • false:對應createReactiveObject的isReadonly參數;
  • mutableHandlers:普通對象和數組的代理處理程序;
  • mutableCollectionHandlers:Set和Map的代理處理程序;
  • reactiveMap:之前定義的全局變量,收集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 (isReadonly(target)) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}

4. 造物主createReactiveObject

不論是reactive,還是shallowReactive、readonly和shallowReadonly,都是內部調用createReactiveObject來創(chuàng)建代理的。createReactiveObject也沒什么操作,主要判斷了下target的類型,再決定是直接返回target還是返回一個新建的proxy。

以下情況直接返回target:

  • target不是對象;
  • target已經是一個響應式的對象,即由createReactiveObject創(chuàng)建的proxy;
  • target類型校驗不合法,例如RegExp、Date等;

當參數proxyMap對應的實參(可能為reactiveMap、shallowReactiveMap、readonlyMap或shallowReadonlyMap,分別對應ractive、shallowReactive、readonly和shallowReadonly四個API)里已經存在了target的響應式對象時,直接取出并返回該響應式對象;

否則,創(chuàng)建一個target的響應式對象proxy,將proxy加入到proxyMap中,然后返回該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 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 specific 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根據普通對象和數組類型、Set和Map類型來區(qū)分baseHandlers和collectionHandlers。

5. shallowReactive、readonly和shallowReadonly

事實上,ractive、shallowReactive、readonly和shallowReadonly這幾個函數形式上基本一致,都是通過createReactiveObject來創(chuàng)建響應式對象,存儲在對應的proxyMap里,但是對應的baseHandlers和collectionHandlers有區(qū)別。

// shallowReactive
export function shallowReactive<T extends object>(
  target: T
): ShallowReactive<T> {
  return createReactiveObject(
    target,
    false,
    shallowReactiveHandlers,
    shallowCollectionHandlers,
    shallowReactiveMap
  )
}
// raedonly
// 注意readonly不是響應式的,而是一個原對象的只讀的拷貝
// 具體實現在對應的handlers里
export function readonly<T extends object>(
  target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
  return createReactiveObject(
    target,
    true,
    readonlyHandlers,
    readonlyCollectionHandlers,
    readonlyMap
  )
}
// shallowReadonly
// 是響應式的
// 只有最外層是只讀的
export function shallowReadonly<T extends object>(target: T): Readonly<T> {
  return createReactiveObject(
    target,
    true,
    shallowReadonlyHandlers,
    shallowReadonlyCollectionHandlers,
    shallowReadonlyMap
  )
}

事實上,ractive、shallowReactive、readonly和shallowReadonly這幾個函數形式上基本一致,都是通過createReactiveObject來創(chuàng)建響應式對象,存儲在對應的proxyMap里,但是對應的baseHandlers和collectionHandlers有區(qū)別。那么我們就知道了,其實重點都在各種handlers里。

二、對應的 Handlers

baseHandlers用于普通對象和數組的代理,collectionHandlers用于Set、Map等的代理。對應ractive、shallowReactive、readonly和shallowReadonly四個API,每一個都有自己的baseHandlers和collectionHandlers。

1. baseHandlers

在packages/reactivity/src/baseHandlers.ts文件中。分別導出了這4個API對應的baseHandlers。

1.1 reactive

reactive的baseHandlers中有5個代理程序。

// reactive
export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}

在攔截過程中,在get、has和ownKey這幾個訪問程序中進行依賴捕獲(track),在set和deleteProperty這倆用于更改的程序中觸發(fā)更新(trigger) 。

get和set分別由函數createGetter和createSetter創(chuàng)建,這倆函數根據入參的不同,返回不同的get和set,readonly等API的baseHandlers中的get和set也大都源于此,除了兩種readonly中用于告警的set。

(1) get

createGetter兩個入參:isReadonly和isShallow,兩兩組合正好對應四個API。

  • shallow:為true時不會進入遞歸環(huán)節(jié),因此是淺層的處理;
  • isReadonly:在createGetter中影響proxyMap的選擇和遞歸時API的選擇,它主要發(fā)揮作用是在set中。
function createGetter(isReadonly = false, shallow = false) {
  return function get(target: Target, key: string | symbol, receiver: object) {
    // 以下幾個if分支判斷target是否已經是由這幾個API創(chuàng)建的代理對象,代理得到的proxy才具有這些key
    if (key === ReactiveFlags.IS_REACTIVE) {
      // 是否是響應式對象
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      // 是否是只讀對象
      return isReadonly
    } else if (key === ReactiveFlags.IS_SHALLOW) {
      // 是否是淺層的 響應式/只讀 對象
      return shallow
    } else if (
      // __v_raw 屬性對應 代理對象的目標對象
      // 當該屬性有值,且在相應的proxyMap中存在代理對象時,說明target已經是一個proxy了
      // __v_raw 屬性對應的值為target本身
      key === ReactiveFlags.RAW &&
      receiver ===
        (isReadonly
          ? shallow
            ? shallowReadonlyMap
            : readonlyMap
          : shallow
          ? shallowReactiveMap
          : reactiveMap
        ).get(target)
    ) {
      return target
    }
    const targetIsArray = isArray(target)
    // 對數組的幾個方法進行代理,在'includes', 'indexOf', 'lastIndexOf'等方法中進行track捕獲依賴
    if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
      return Reflect.get(arrayInstrumentations, key, receiver)
    }
    const res = Reflect.get(target, key, receiver)
    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res
    }
    // 如果不是readonly,則捕獲依賴,因此,readonly 為非響應式的
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }
    if (shallow) {
      return res
    }
    // 如果get到的值是一個Ref,會直接解包,無需再使用 .value 來獲取真正需要的值
    // 除非目標對象target是數組,或者當前的key是整數
    // 例如,obj[0],即使是一個Ref也不會直接解包,使用的時候依然要 obj[0].value
    // shallow沒有走到這一步,因此也不會自動解包
    if (isRef(res)) {
      // ref unwrapping - skip unwrap for Array + integer key.
      return targetIsArray && isIntegerKey(key) ? res : res.value
    }
    // 當get到的值是對象時,根據是否是readonly來遞歸操作,需要防止對象循環(huán)引用
    // shallow沒有走到這一步,因此shallow是淺層的
    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
  }
}

(2) set

對于reactive,可以說最主要的任務就是在set中觸發(fā)更新,set包括 新增 和 修改 屬性值。如果當前的key對應的值是一個Ref,且其它條件滿足時,則觸發(fā)更新的操作是在Ref的內部。這些在后續(xù)講解Ref的時候會提到。

function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    let oldValue = (target as any)[key]
    // 當前值是Readonly的Ref,而新值不是Ref時,不允許修改
    if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
      return false
    }
    // 如果是深層的修改
    if (!shallow) {
      // 解出原本的非proxy值
      if (!isShallow(value) && !isReadonly(value)) {
        oldValue = toRaw(oldValue)
        value = toRaw(value)
      }
      // 目標對象非數組,當前key的值是Ref而新值不是Ref,則通過 .value 賦值
      // 在Ref內部觸發(fā)更新
      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
    }
    // 然后是觸發(fā)更新的部分了
    // 判斷當前key是否已經存在于target上
    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
    // 如果是原型鏈上的字段則不會觸發(fā)更新
    if (target === toRaw(receiver)) {
      if (!hadKey) {
        // 當前的key已經存在,觸發(fā)新增的更新
        trigger(target, TriggerOpTypes.ADD, key, value)
      } else if (hasChanged(value, oldValue)) {
        // 當前key不存在,觸發(fā)修改的更新
        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
      }
    }
    return result
  }
}

(3) deleteProperty

刪除操作的代理程序,和set一樣,deleteProperty攔截delete和Reflect.deleteProperty()操作,它也能觸發(fā)更新。

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)
  // 刪除成功 且 target中原來有這個屬性時,觸發(fā)刪除的更新
  if (result && hadKey) {
    trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
  }
  return result
}

(4) has

has用于判斷target中是否有當前的key,攔截a in obj、with(obj){(a)}、Reflect.has等操作,屬于訪問程序,在其中進行has操作的依賴收集。

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
}

(5) ownKeys

用于獲取target所有自身擁有的key,攔截Object.getOwnPropertyNames、Object.getOwnPropertySymbols、Object.keys、Reflect.ownKeys,屬于訪問程序,在其中進行迭代的依賴收集。

function ownKeys(target: object): (string | symbol)[] {
  track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
  return Reflect.ownKeys(target)
}

現在我們算是都弄明白了,對于普通對象和數組,reactive創(chuàng)建proxy,通過get、set、deleteProperty、has、ownKeys五個代理處理程序,來攔截其屬性訪問操作,在其中進行依賴收集,攔截其增刪改操作,其中觸發(fā)更新。

1.2 readonly

readonly的代理處理程序只有三個:

  • get:由createGetter(true)創(chuàng)建,還記得我們上面講到的createSetter嗎?
  • set
  • deleteProperty:這兩個代理處理程序用于告警,畢竟readonly不可修改。

畢加思索一下createGetter(true),傳入的readonly=true,使得get中不會進行track操作來收集依賴,因而不具有響應性。

const readonlyGet = /*#__PURE__*/ createGetter(true)
export const readonlyHandlers: ProxyHandler<object> = {
  get: readonlyGet,
  set(target, key) {
    if (__DEV__) {
      warn(
        `Set operation on key "${String(key)}" failed: target is readonly.`,
        target
      )
    }
    return true
  },
  deleteProperty(target, key) {
    if (__DEV__) {
      warn(
        `Delete operation on key "${String(key)}" failed: target is readonly.`,
        target
      )
    }
    return true
  }
}

1.3 shallowReactive

shallowReactive移植了reactive的baseHandlers,并且更新了get和set。具體實現也可以回顧上面說到的createGetter和createSetter。

回過頭來看看createGetter(false, true),isReadonly = false,則在get中,可以進行track依賴收集;shallow = true,則在get中不會對頂層的Ref進行解包,也不會進行遞歸操作。

而在createSetter(true)中,參數shallow幾乎只影響是否要解出原本的raw值。如果新值value不是淺層且不是只讀的,則需要解出它的原本raw值,之后才能進行賦值操作,否則我們的shallowRef將不再是淺層的了。

const shallowGet = /*#__PURE__*/ createGetter(false, true)
const shallowSet = /*#__PURE__*/ createSetter(true)
export const shallowReactiveHandlers = /*#__PURE__*/ extend(
  {},
  mutableHandlers,
  {
    get: shallowGet,
    set: shallowSet
  }
)

1.4 shallowReadonly

移植了readonly的baseHandlers,更新了其中的get,這個get也試試由createGetter創(chuàng)建。我們知道,readonly的baseHandlers里,除了get,另外倆都是用來攔截修改操作并告警的。

回顧一下createGetter,當isReadonly===true時,不會進行track操作來收集依賴;shallow===true時,不會對Ref進行解包,也不會走到遞歸環(huán)節(jié),即是淺層的readonly。

const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
// Props handlers are special in the sense that it should not unwrap top-level
// refs (in order to allow refs to be explicitly passed down), but should
// retain the reactivity of the normal readonly object.
export const shallowReadonlyHandlers = /*#__PURE__*/ extend(
  {},
  readonlyHandlers,
  {
    get: shallowReadonlyGet
  }
)

2. cellectionHandlers

對于Set和Map較為復雜的數據結構,他們有自己的方法,因此代理程序會有些差別。基本都是攔截它們原本的方法,然后進行track或trigger。可以看到這幾個handlers中,都只有由createInstrumentationGetter創(chuàng)建的get。

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

1.1 createInstrumentationGetter

因為是代理Set和Map,在攔截它們的實例方法之前,對實例的訪問,即get,這個get并非Map或Set實例的get方法,而是表示對實例的訪問操作。

例如:

const map = new Map([['name', 'cc']]);

map.set('age', 18);

這里map.set()首先就是訪問map的set方法,對應的key就是字符串'set',而這一步就會被代理的get程序攔截,而真正的對方法的攔截,都在相應的instrumentations里預設好了。攔截了之后,如果key在instrumentations里存在,返回預設的方法,在其中進行track和trigger操作,否則是其它屬性/方法,直接返回即可,不會進行track和trigger。

const [
  mutableInstrumentations,
  readonlyInstrumentations,
  shallowInstrumentations,
  shallowReadonlyInstrumentations
] = /* #__PURE__*/ createInstrumentations()
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
    )
  }
}

1.2 instrumentations

和baseHandlers相比,Proxy無法直接攔截Map和Set的方法的調用,而是通過get程序來攔截,再判斷key是否為執(zhí)行增刪改查的方法,從而判斷是否進行依賴收集或更新。因此,就需要先預設好,哪些key作為方法名時可以觸發(fā)track和trigger。其實也就是Map和Set的那些實例方法和迭代器方法。而各種Instrumentations,就是這些預設的方法,track和trigger操作都在其中。

function createInstrumentations() {
  // 對應reactive
  const mutableInstrumentations: Record<string, Function> = {
    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)
  }
  // 對應shallowReactive
  const shallowInstrumentations: Record<string, Function> = {
    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)
  }
  // 對應readonly
  const readonlyInstrumentations: Record<string, Function> = {
    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)
  }
  // 對應shallowReadonly
  const shallowReadonlyInstrumentations: Record<string, Function> = {
    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)
  }
  // 使用 createIterableMethod 給這些 Instrumentations 掛上幾個迭代器
  const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
  iteratorMethods.forEach(method => {
    mutableInstrumentations[method as string] = createIterableMethod(
      method,
      false,
      false
    )
    readonlyInstrumentations[method as string] = createIterableMethod(
      method,
      true,
      false
    )
    shallowInstrumentations[method as string] = createIterableMethod(
      method,
      false,
      true
    )
    shallowReadonlyInstrumentations[method as string] = createIterableMethod(
      method,
      true,
      true
    )
  })
  return [
    mutableInstrumentations,
    readonlyInstrumentations,
    shallowInstrumentations,
    shallowReadonlyInstrumentations
  ]
}

函數createInstrumentations分為兩部分,前部分是利用已有的get、set、add、has、clear等等來得到各個instrumentations,后部分是對各個instrumentations中的迭代方法的更新。只要不是isReadonly不是真值,則無論是get、set等方法還是keys、values等迭代器接口,都在內部進行了track或trigger,當然,get、has、size等方法 和 幾個迭代器方法都屬于訪問操作,因此內部是使用track來收集依賴,而trigger發(fā)生在增、刪、改操作里,當然,也要根據isReadonly和shallow有所區(qū)分,思路基本和baseHandlers一致。

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)
  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)
  }
}
function has(this: CollectionTypes, key: unknown, isReadonly = false): boolean {
  const target = (this as any)[ReactiveFlags.RAW]
  const rawTarget = toRaw(target)
  const rawKey = toRaw(key)
  if (!isReadonly) {
    if (key !== rawKey) {
      track(rawTarget, TrackOpTypes.HAS, key)
    }
    track(rawTarget, TrackOpTypes.HAS, rawKey)
  }
  return key === rawKey
    ? target.has(key)
    : target.has(key) || target.has(rawKey)
}
function size(target: IterableCollections, isReadonly = false) {
  target = (target as any)[ReactiveFlags.RAW]
  !isReadonly && track(toRaw(target), TrackOpTypes.ITERATE, ITERATE_KEY)
  return Reflect.get(target, 'size', target)
}
function add(this: SetTypes, value: unknown) {
  value = toRaw(value)
  const target = toRaw(this)
  const proto = getProto(target)
  const hadKey = proto.has.call(target, value)
  if (!hadKey) {
    target.add(value)
    trigger(target, TriggerOpTypes.ADD, value, value)
  }
  return this
}
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
}
function deleteEntry(this: CollectionTypes, key: unknown) {
  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 ? get.call(target, key) : undefined
  // forward the operation before queueing reactions
  const result = target.delete(key)
  if (hadKey) {
    trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
  }
  return result
}
function clear(this: IterableCollections) {
  const target = toRaw(this)
  const hadItems = target.size !== 0
  const oldTarget = __DEV__
    ? isMap(target)
      ? new Map(target)
      : new Set(target)
    : undefined
  // forward the operation before queueing reactions
  const result = target.clear()
  if (hadItems) {
    trigger(target, TriggerOpTypes.CLEAR, undefined, undefined, oldTarget)
  }
  return result
}

1.3 createIterableMethod

這里稍微提一下createIterableMethod,用于利用Map和Set本身的迭代器方法,并做了一點修改,在其中加入了track來收集依賴。

function createIterableMethod(
  method: string | symbol,
  isReadonly: boolean,
  isShallow: boolean
) {
  return function (
    this: IterableCollections,
    ...args: unknown[]
  ): Iterable & Iterator {
    const target = (this as any)[ReactiveFlags.RAW]
    const rawTarget = toRaw(target)
    const targetIsMap = isMap(rawTarget)
    const isPair =
      method === 'entries' || (method === Symbol.iterator && targetIsMap)
    const isKeyOnly = method === 'keys' && targetIsMap
    const innerIterator = target[method](...args)
    const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
    !isReadonly &&
      track(
        rawTarget,
        TrackOpTypes.ITERATE,
        isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY
      )
    // return a wrapped iterator which returns observed versions of the
    // values emitted from the real iterator
    return {
      // iterator protocol
      next() {
        const { value, done } = innerIterator.next()
        return done
          ? { value, done }
          : {
              value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),
              done
            }
      },
      // iterable protocol
      [Symbol.iterator]() {
        return this
      }
    }
  }
}

小結

分析完各個部分,可以看到,無論是baseHandlers還是collectionHandlers,思路都是一致的。

但是collectionHandlers只有get這一個代理程序,通過攔截到的key判斷是否是Map和Set實例自帶的增刪改查的方法,從而返回預設好的hack版本的方法或原本的屬性值,然后繼續(xù)后續(xù)的操作。在hack版本的方法里進行track和trigger。

以上就是Vue3 源碼分析reactive readonly實例的詳細內容,更多關于Vue3 reactive readonly的資料請關注腳本之家其它相關文章!

相關文章

  • 解決el-tree節(jié)點過濾不顯示下級的問題

    解決el-tree節(jié)點過濾不顯示下級的問題

    這篇文章主要介紹了解決el-tree節(jié)點過濾不顯示下級的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • 解決vue $http的get和post請求跨域問題

    解決vue $http的get和post請求跨域問題

    這篇文章主要介紹了解決vue $http的get和post請求跨域問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 派發(fā)器抽離vue2單組件中的大量邏輯技巧

    派發(fā)器抽離vue2單組件中的大量邏輯技巧

    這篇文章主要為大家介紹了派發(fā)器抽離vue2單組件中的大量邏輯技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • 解決elementui導航折疊卡頓的問題

    解決elementui導航折疊卡頓的問題

    這篇文章主要介紹了解決elementui導航折疊卡頓的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • vue列表如何自動滾動到制定位置

    vue列表如何自動滾動到制定位置

    這篇文章主要介紹了vue列表如何自動滾動到制定位置問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • vue diff算法全解析

    vue diff算法全解析

    這篇文章主要介紹了vue diff算法的使用,幫助大家更好的理解和學習使用vue,感興趣的朋友可以了解下
    2021-04-04
  • vue頁面切換過渡transition效果

    vue頁面切換過渡transition效果

    這篇文章主要介紹了vue頁面切換過渡transition效果,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-10-10
  • Vue+thinkphp5.1+axios實現文件上傳

    Vue+thinkphp5.1+axios實現文件上傳

    這篇文章主要為大家詳細介紹了Vue+thinkphp5.1+axios實現文件上傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • Vue中動畫與過渡的使用教程

    Vue中動畫與過渡的使用教程

    最近在寫vue的一個項目要實現過渡的效果,雖然vue動畫不是強項,庫也多,但是基本的坑還是得踩扎實,下面這篇文章主要給大家介紹了關于Vue中實現過渡動畫效果的相關資料,需要的朋友可以參考下
    2023-01-01
  • vue?parseHTML函數解析器遇到結束標簽

    vue?parseHTML函數解析器遇到結束標簽

    這篇文章主要介紹了vue?parseHTML函數源碼解析之析器遇到結束標簽的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07

最新評論