目录引言一、reactive和readonly1.reactive相关类型2.相关全局变量与方法3.reactive函数4.造物主createReactiveObject5.shallo...
目录
引言一、reactive 和 readonly
1. reactive相关类型
2. 相关全局变量与方法
3. reactive函数
4. 造物主createReactiveObject
5. shallowReactive、readonly和shallowReadonly
二、对应的 Handlers
1. baseHandlers
1.1 reactive
1.2 readonly
1.3 shallowReactive
1.4 shallowReadonly
2. cellectionHandlers
1.1 createInstrumentationGetter
1.2 instrumentations
1.3 createIterableMethod
小结
引言
上次一起阅读了watch和computed的源码,其实应该先看副作用effect,因为各个响应式的API里基本都用到了,等结束了reactive和readonly和ref,就一起看看effect。这次要说的是reactive和readonly,两者在实现上流程大体一致。尤其是对Map和Set的方法的代理拦截,多少有点妙。
一、reactive 和 readonly
vue3使用Proxy来替代Vue2中Object.defineProperty。
const target = {
name: 'onlyy~'
}
// 创建一个对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字段,该字段表示它原本的非响应式状态的值;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),便于后续判断某个对象是否存在已创建的响应式对象;
TargetType:枚举成员的内容分别用于区分代理目标是否校验合法、普通对象、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来创建响应式对象。瞄一眼入参有五个:
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来创建代理的。createReactiveObject也没什么操作,主要判断了下target的类型,再决定是直接返回target还是返回一个新建的proxy。
以下情况直接返回target:
target不是对象;target已经是一个响应式的对象,即由createReactiveObject创建的proxy;
target类型校验不合法,例如RegExp、Date等;
当参数proxyMap对应的实参(可能为reactiveMap、shallowReactiveMap、readonlyMap或shallowReadonlyMap,分别对应ractive、shallowReactive、readonly和shallowReadonly四个API)里已经存在了target的响应式对象时,直接取出并返回该响应式对象;
否则,创建一个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类型来区分baseHandlers和collectionHandlers。
5. shallowReactive、readonly和shallowReadonly
事实上,ractive、shallowReactive、readonly和shallowReadonly这几个函数形式上基本一致,都是通过createReactiveObject来创建响应式对象,存储在对应的proxyMap里,但是对应的baseHandlers和collectionHandlers有区别。
// 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,
www.cppcns.com shallowReadonlyMap
)
}
事实上,ractive、shallowReactive、readonly和shallowReadonly这几个函数形式上基本一致,都是通过createReactiveObject来创建响应式对象,存储在对应的proxyMap里,但是对应的baseHandlers和collectionHandlers有区别。那么我们就知道了,其实重点都在各种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这俩用于更改的程序中触发更新(trigger) 。
get和set分别由函数createGetter和createSetter创建,这俩函数根据入参的不同,返回不同的get和set,readonly等API的baseHandlers中的get和set也大都源于此,除了两种readonly中用于告警的set。
(1) get
createGetter两个入参:isReadonly和isShallow,两两组合正好对应四个API。
shallow:为true时不会进入递归环节,因此是浅层的处理;isReadonly:在createGetter中影响proxyMap的选择和递归时API的选择,它主要发挥作用是在set中。
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
// 以下几个if分支判断target是否已经是由这几个API创建的代理对象,代理得到的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来递归操作,需要防止对象循环引用
// 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中触发更新,set包括 新增 和 修改 属性值。如果当前的key对应的值是一个Ref,且其它条件满足时,则触发更新的操作是在Ref的内部。这些在后续讲解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内部触发更新
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
}
// 然后是触发更新的部分了
// 判断当前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
// 如果是原型链上的字段则不会触发更新
if (target === toRaw(receiver)) {
if (!hadKey) {
// 当前的key已经存在,触发新增的更新
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 当前key不存在,触发修改的更新
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
(3) deleteProperty
删除操作的代理程序,和set一样,deleteProperty拦截delete和Reflect.deleteProperty()操作,它也能触发更新。
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中原来有这个属性时,触发删除的更新
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创建proxy,通过get、set、deleteProperty、has、ownKeys五个代理处理程序,来拦截其属性访问操作,在其中进行依赖收集,拦截其增删改操作,其中触发更新。
1.2 readonly
readonly的代理处理程序只有三个:
get:由createGetter(true)创建,还记得我们上面讲到的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创建。我们知道,readonly的baseHandlers里,除了get,另外俩都是用来拦截修改操作并告警的。
回顾一下createGetter,当isReadonly===true时,不会进行track操作来收集依赖;shallow===true时,不会对Ref进行解包,也不会走到递归环节,即是浅层的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创建的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是否为执行增删改查的方法,从而判断是否进行依赖收集或更新。因此,就需要先预设好,哪些key作为方法名时可以触发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发生在增、删、改操作里,当然,也要根据isReadonly和shallow有所区分,思路基本和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版本的方法或原本的属性值,然后继续后续的操作。在hack版本的方法里进行track和trigger。
以上就是Vue3 源码分析reactive readonly实例的详细内容,更多关于Vue3 reactive readonly的资料请关注我们其它相关文章!










