# Reactive
Reactivity Api是Vue3.x中创建响应式对象的核心api,它的基本实现原理是通过Proxy来拦截对象的操作,并由此收集依赖或派发更新。 Reactivity Api的实现并没有那么复杂,只需记住通过Reactivity Api创建的对象都是Proxy对象,它的核心api有以下几个:
1、reactive: 返回原始对象的Proxy代理对象,支持收集依赖和派发更新,访问自身属性时会执行嵌套对象的深度响应式转换。 2、shallowReactive: 返回原始对象的Proxy代理对象,但只拦截对象根层级的属性的操作,如果属性的值也是对象,不会对它进行响应式转换。 3、readonly: 返回原始对象的Proxy代理对象,限制赋值操作,访问它的任何嵌套属性也将是只读的。 4、shallowReadonly: 返回原始对象的Proxy代理对象,只限制对象根层级的属性的set操作,但不执行嵌套对象的深度只读转换。
Reactivity api除了支持基本的plain object和array外,还支持map、weakmap、set、weakset等collection的响应式化。我们可以通过它的测试用例来了解reactive以及它相关api的用法,这对我们学习源码很有帮助。
# 一些概念
普通对象:Object、Array、Map、Weakmap、Set、Weakset中的一种 代理对象:普通对象传入Reactivity Api后创建并返回的Proxy对象 原始对象:创建Proxy对象的target对象,原始对象有可能是普通对象也可能是代理对象
# 相关源码
// 定义了传入Proxy的原始对象target上可能出现的一些属性的类型,这里需要注意的是,传入的target可能是一个普通对象也可能
// 代理对象,那么就需要一些属性来判断target的类型
// 通过ReactiveFlags枚举了这些属性的名字,它们的含义如下:
// SKIP: 当传入的原始对象target存在此属性则跳过只读或者响应式化
// IS_REACTIVE: 当此属性为true时,则target已经是一个reactive代理对象
// IS_READONLY: 当此属性为true时,则target已经是一个readonly代理对象
// RAW: 当target是一个代理对象时,通过此属性获得原始对象的值
// REACTIVE: 当原始对象target是一个普通对象,但是它已经被响应式化过了,通过此属性可获得target的响应式代理对象
// READONLY: 当原始对象target是一个普通对象,但是它已经被只读化过了,通过此属性可获得target的只读代理对象
export const enum ReactiveFlags {
SKIP = '__v_skip',
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly',
RAW = '__v_raw',
REACTIVE = '__v_reactive',
READONLY = '__v_readonly'
}
interface Target {
[ReactiveFlags.SKIP]?: boolean
[ReactiveFlags.IS_REACTIVE]?: boolean
[ReactiveFlags.IS_READONLY]?: boolean
[ReactiveFlags.RAW]?: any
[ReactiveFlags.REACTIVE]?: any
[ReactiveFlags.READONLY]?: any
}
// isObservableType存放了可以响应式化的对象的类型
// 其中又区分了collectionTypes,它们的实现与普通的Object和Array不同,后面我们会细说
const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
const isObservableType = /*#__PURE__*/ makeMap(
'Object,Array,Map,Set,WeakMap,WeakSet'
)
// 通过canObserve来判断一个对象是否能被响应式化,成为响应式对象必须满足以下条件:
// 1、对象上不存在ReactiveFlags.SKIP属性
// 2、它的类型必须是Object,Array,Map,Set,WeakMap,WeakSet中的一种
// 3、对象没有被冻结
const canObserve = (value: Target): boolean => {
return (
!value[ReactiveFlags.SKIP] &&
isObservableType(toRawType(value)) &&
!Object.isFrozen(value)
)
}
接下来是几个创建响应式对象的api,这里重点注意创建不同类型的响应式对象的api返回的类型不同,关于类型的详细解析我们放到后面细讲。
// 通过reactive方法创建普通的mutable reactive,如果传入的对象上存在ReactiveFlags.IS_READONLY属性
// 也就是说它已经是一个只读的响应式对象,则直接返回,否则
// 通过调用createReactiveObject方法来创建响应式对象
// 这里要注意的是reactive方法返回的类型是UnwrapNestedRefs<T>,
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 (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers
)
}
// 通过shallowReactive方法创建一个shallow reactive对象,我们已经在前面介绍过它
// 可以看到shallowReactive方法返回的类型和传入的类型T是一致的
export function shallowReactive<T extends object>(target: T): T {
return createReactiveObject(
target,
false,
shallowReactiveHandlers,
shallowCollectionHandlers
)
}
// 通过readonly方法创建一个readonly对象,我们已经在前面介绍过它
// 可以看到readonly方法返回的类型是DeepReadonly<UnwrapNestedRefs<T>>
export function readonly<T extends object>(
target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
return createReactiveObject(
target,
true,
readonlyHandlers,
readonlyCollectionHandlers
)
}
// 通过shallowReadonly方法创建一个shallow readonly对象,我们已经在前面介绍过它
// 可以看到shallowReadonly方法返回的类型是Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }>
export function shallowReadonly<T extends object>(
target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
return createReactiveObject(
target,
true,
shallowReadonlyHandlers,
readonlyCollectionHandlers
)
}
不难看出以上的几个主要创建响应式对象的api都是内部调用了createReactiveObject方法,根据传入的参数不同来创建不同类型的响应式对象。
// 可以看到createReactiveObject方法传入了4个参数
// target: 传入的将要响应式化的原始对象,它可能是一个响应式对象也可能是普通对象
// isReadonly: 是否创建只读的响应式对象
// baseHandlers: 普通的Object和Array的proxy handles对象
// collectionHandlers: Map、Weakmap、Set、Weakset类型的proxy handles对象
// 后面我们会详细分析不同类型的响应式对象的baseHandlers和collectionHandlers的实现
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>
) {
// 在开发环境中,如果传入的target不是对象,则发出警告并直接返回传入的target
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// 当target上的ReactiveFlags.RAW属性的值存在,则证明target已经是一个reactive或者readonly对象
// 那么当除了isReadonly为true并且target上的ReactiveFlags.IS_REACTIVE属性存在值时,都应该直接返回target
// 意思就是说满足以下情况时都应该直接返回传入的target对象,也就是b === a
// const a = reactive({})
// const b = reactive(a)
// 或
// const a = readonly({})
// const b = readonly(a)
// 或
// const a = readonly({})
// const b = reactive(a)
// 除了以下情况将一个reactive对象传入readonly api
// const a = reactive({a: 1})
// const b = readonly(a)
// 此时会将proxy a作为target来创建proxy b
// 这样当我们收集了b.a作为依赖时,就可以通过修改a.a的值来派发更新,这样作为只读对象的b也可以当做一个响应式对象来使用
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// 如果target已经响应式或者只读化过(这里注意和上面条件的区别),那么target上应该存在
// ReactiveFlags.READONLY或者ReactiveFlags.REACTIVE属性的值,如果存在直接返回属性的值
// 也就是说多次传入origin,都应该返回同一reactive对象,也就是b === a
// const origin = { a: 1}
// const a = reactive(origin)
// const b = reactive(origin)
const reactiveFlag = isReadonly
? ReactiveFlags.READONLY
: ReactiveFlags.REACTIVE
if (hasOwn(target, reactiveFlag)) {
return target[reactiveFlag]
}
// 最后一个条件,判断是否可以响应式或者只读化
if (!canObserve(target)) {
return target
}
// 传入target生成proxy对象,注意这里区分的collectionHandlers和baseHandlers
const observed = new Proxy(
target,
collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
)
将生成的proxy对象保存到target的ReactiveFlags.READONLY或者ReactiveFlags.REACTIVE属性上
def(target, reactiveFlag, observed)
return observed
}
其他几个经常使用到的Api,后面的源码中也经常用到:
// 判断是一个对是不是响应式代理对象
export function isReactive(value: unknown): boolean {
// 如果对象是一个只读对象,那么获取它的原始对象
// 如果原始对象本身是一个响应式代理对象,那么也返回true
if (isReadonly(value)) {
return isReactive((value as Target)[ReactiveFlags.RAW])
}
// 通过value上ReactiveFlags.IS_REACTIVE属性判断
return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}
// 判断一个对象是不是一个只读代理对象
export function isReadonly(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}
// 判断一个对象是不是一个代理对象
export function isProxy(value: unknown): boolean {
return isReactive(value) || isReadonly(value)
}
// 获取代理对象的原始对象,如果不存在则返回本身
export function toRaw<T>(observed: T): T {
return (
(observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
)
}
export function markRaw<T extends object>(value: T): T {
def(value, ReactiveFlags.SKIP, true)
return value
}
# baseHandlers
当传入Reactivity Api的原始对象的类型是Object或者Array时,创建Proxy对象时传入的handlers为baseHandlers, 根据api不同,传入的baseHandlers也分为以下几种:
1、传入reactive api的mutableHandlers 2、传入shallowReactive api的shallowReactiveHandlers 3、传入readonly api的readonlyHandlers 4、传入shallowReadonly api的shallowReadonlyHandlers
baseHandles的代码存放在packages/src/reactivity/baseHandlers.ts中
# 相关源码
// 获取Symbol对象上所有的内建symbol值
const builtInSymbols = new Set(
Object.getOwnPropertyNames(Symbol)
.map(key => (Symbol as any)[key])
.filter(isSymbol)
)
// 定义了handles的get方法,它们都是调用createGetter方法生成的函数,只是传入的参数不同
const get = /*#__PURE__*/ createGetter()
const shallowGet = /*#__PURE__*/ createGetter(false, true)
const readonlyGet = /*#__PURE__*/ createGetter(true)
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
// 重写了Array上的几个方法并保存在arrayInstrumentations上
// 主要的目的就是当调用这几个方法时,需要将数组上每一个key都添加为依赖
// 试想一下在一个响应式的数组上面调用了includes方法查找某个值是否存在时
// 那么数组上任意一个值的修改都有可能改变includes的结果,其他方法也是同理
const arrayInstrumentations: Record<string, Function> = {}
;['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
arrayInstrumentations[key] = function(...args: any[]): any {
// 这里的this值为调用以上方法的proxy对象,也就是reactive或者readonly对象
// 通过toRaw返回ReactiveFlags.RAW属性上面原始对象的值
// 遍历所有的key,并通过track方法将所有的key添加为依赖
const arr = toRaw(this) as any
for (let i = 0, l = (this as any).length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '')
}
// we run the method using the original args first (which may be reactive)
// 通过key值拿到对应的方法并执行,这里也可以看到只要参数存在在原始的arr上,那么不管
// 传入参数本身还是参数的proxy对象都可以获得相同的结果
const res = arr[key](...args)
if (res === -1 || res === false) {
// if that didn't work, run it again using raw values.
return arr[key](...args.map(toRaw))
} else {
return res
}
}
})
// createGetter的参数有isReadonly和shallow,根据这两个参数来创建不同的get方法
// 这里创建的get方法接收了3个参数也就是创建Proxy对象时传入的handlers对象的get方法一样
function createGetter(isReadonly = false, shallow = false) {
return function get(target: object, key: string | symbol, receiver: object) {
// 当访问了proxy上的ReactiveFlags.IS_REACTIVE或者ReactiveFlags.IS_READONLY属性时,直接返回对应的值
// 这里也可以看到这两个属性并不直接添加到reactive或者readonly对象上,而是通过拦截get方法直接返回的
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (
// 当访问proxy上的ReactiveFlags.RAW属性时,并且receiver和通过target创建的reactive或者readonly对象相等
// 则直接返回target,前面也说过了通过ReactiveFlags.RAW属性可以获得创建reactive或者readonly对象的原始对象
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? (target as any)[ReactiveFlags.READONLY]
: (target as any)[ReactiveFlags.REACTIVE])
) {
return target
}
// 当原始对象target是一个数组,并且key是'includes', 'indexOf', 'lastIndexOf'中的一个
// 则执行上面定义的arrayInstrumentations中的方法
const targetIsArray = isArray(target)
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
// 如果是普通对象则通过Reflect.get拿到属性值res
const res = Reflect.get(target, key, receiver)
// 如果key是内建的Symbols或者是__proto__、__v_isRef(ref对象的标志)
// 则直接返回res,访问这些属性不会将这些key收集为依赖
if (
isSymbol(key)
? builtInSymbols.has(key)
: key === `__proto__` || key === `__v_isRef`
) {
return res
}
// 如果不是readonly的对象,那么调用tarck方法收集依赖
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
// 如果是shallow为true,也就是shallowReactiveHandlers或者shallowReadonlyHandlers,到这里就返回了
if (shallow) {
return res
}
// 如果属性的值是ref类型,那么在Object中应该是默认展开的(ref会在后面的章节介绍,这里先了解即可)
if (isRef(res)) {
// ref unwrapping, only for Objects, not for Arrays.
return targetIsArray ? res : res.value
}
// 如果属性的值是一个对象,那么应该继续对res进行响应式或只读转换
// 前面介绍api的时候也有说过了
// 这里也可以和Vue2.x的实现对比一下,以前是只要在data中定义了那么初始化的时候会递归遍历整个对象进行响应式转化
// 而3.x只有访问了属性并且属性的值是一个对象,才会继续进行响应式转化
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
}
}
// 接下来是set方法
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 {
// 先拿到旧值
const oldValue = (target as any)[key]
// 当设置的旧值原本是一个ref类型而新值不是ref时,那么新值应该设置到旧值的value属性上
if (!shallow) {
// 当设置的新值是reactive或者readonly对象时,则需要通过toRaw获取他们的原始对象
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
// 如果是shallow模式,则不需要处理设置的新值,因为shallow模式的响应式对象只需拦截根层级的属性的操作
}
// 判断key值是不是已经存在
const hadKey = hasOwn(target, key)
// 通过Reflect.set设置新值
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
// 分析下面的代码前我们先看下面的例子:
// let dummy
// const a = { c: 1 }
// const b = reactive({ d: 1 })
// Object.setPrototypeOf(a, b)
// effect(() => {
// dummy = b.d
// console.log(dummy)
// })
// a.d = 2
// 这个例子的结果是,只会在输出一次dummy的值 1
// 当普通对象的原型是一个proxy对象时,对普通对象的操作也会被原型上的proxy对象拦截
// 这就导致了,我们往普通对象a上添加一个key为d的属性,会触发原型链上proxy b的set方法,proxy b上同样也有d属性
// 如果在这里派发更新很显然是不合理的,因为我们只是在操作普通对象a而已,所以这里才需要加一个判断条件
// 当触发原型链上proxy b的set方法时,target为proxy b的原始对象{ d: 1 },但是由于我们是通过a对象访问d属性的
// 那么receiver就是a对象,所以target是肯定不等于toRaw(receiver)的,因此这里并不会派发更新
if (target === toRaw(receiver)) {
// 调用trigger方法派发更新,根据是修改还是添加key传入不同的参数
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
// 拦截删除操作的方法,这里没什么要注意的地方
// 如果key存在并且删除成功,则调用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
}
// 使用in操作符时会触发has方法,除了key的值为symbol外 都要调用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
}
// 遍历对象的key值时会触发ownKeys方法,调用track方法收集依赖,返回Reflect.ownKeys的执行结果
function ownKeys(target: object): (string | number | symbol)[] {
track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.ownKeys(target)
}
// 根据不同的的proxy对象组合handlers的功能,mutableHandlers包含了以上所有的方法
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
// readonlyHandlers传入的是readonlyGet,并且重写了set和deleteProperty,在开发环境修改readonly对象的时会有警告
export const readonlyHandlers: ProxyHandler<object> = {
get: readonlyGet,
has,
ownKeys,
set(target, key) {
if (__DEV__) {
console.warn(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
},
deleteProperty(target, key) {
if (__DEV__) {
console.warn(
`Delete operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
}
}
// shallowReactiveHandlers除了get、set方法其他与mutableHandlers相同
export const shallowReactiveHandlers: ProxyHandler<object> = extend(
{},
mutableHandlers,
{
get: shallowGet,
set: shallowSet
}
)
// 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: ProxyHandler<object> = extend(
{},
readonlyHandlers,
{
get: shallowReadonlyGet
}
)
# collectionHandlers
当传入Reactivity Api的原始对象的类型是Map、Weakmap、Set、Weakset时,创建Proxy对象时传入的handlers为collectionHandlers, collectionHandlers的代码存放在packages/src/reactivity/collectionHandlers.ts中,根据api不同, 传入的collectionHandlers也分为以下几种:
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: createInstrumentationGetter(false, false)
}
export const shallowCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: createInstrumentationGetter(false, true)
}
export const readonlyCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: createInstrumentationGetter(true, false)
}
// 可以看到以上几种handlers都只定义了get方法,因为本身所有的collection类型的对象比如Map、Set都只能通过方法
// 来操作对象,比如说set.add()、set.get()等等,所以只需要拦截对象的get操作就行了
// 通过createInstrumentationGetter来创建不同类型的get方法
function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
// 通过传入的参数返回不同的instrumentations
// instrumentations定义了重写的collection类型的对象上的方法
// 后面会详细分析
const instrumentations = shallow
? shallowInstrumentations
: isReadonly
? readonlyInstrumentations
: mutableInstrumentations
// 返回创建的get方法
return (
target: CollectionTypes,
key: string | symbol,
receiver: CollectionTypes
) => {
// 这里的实现和baseHandlers一样
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (key === ReactiveFlags.RAW) {
return target
}
// 如果访问的key是存在在instrumentations上的,那么获取的是instrumentations上的方法,否则
// 获取的是target上的方法
return Reflect.get(
hasOwn(instrumentations, key) && key in target
? instrumentations
: target,
key,
receiver
)
}
}
const mutableInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key, toReactive)
},
get size() {
return size((this as unknown) as IterableCollections)
},
has,
add,
set,
delete: deleteEntry,
clear,
forEach: createForEach(false, false)
}
const shallowInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key, toShallow)
},
get size() {
return size((this as unknown) as IterableCollections)
},
has,
add,
set,
delete: deleteEntry,
clear,
forEach: createForEach(false, true)
}
const readonlyInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key, toReadonly)
},
get size() {
return size((this as unknown) as IterableCollections)
},
has,
add: createReadonlyMethod(TriggerOpTypes.ADD),
set: createReadonlyMethod(TriggerOpTypes.SET),
delete: createReadonlyMethod(TriggerOpTypes.DELETE),
clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
forEach: createForEach(true, false)
}
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
)
})
// 可以从各个类型的instrumentations对象中看出,上面基本重写了Map、Set操作对象的方法
// 当访问Map、Set等类型的Proxy对象的方法时,会被定义在handlers上的get拦截,并根据key值返回
// instrumentations上重写的方法,接下来我们一个个方法分析,看看Vue如何重写这些方法
const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value
const toReadonly = <T extends unknown>(value: T): T =>
isObject(value) ? readonly(value) : value
const toShallow = <T extends unknown>(value: T): T => value
const getProto = <T extends CollectionTypes>(v: T): any =>
Reflect.getPrototypeOf(v)
// 重写Map.prototype.get() / WeakMap.prototype.get()
// target: 从上文看到这里传入的target是this,也就是调用重写的get方法的proxy对象
// key: get方法传入的key值
// wrap: 根据不同的handles对应传入toReactive、toReadonly、toShallow方法
function get(
target: MapTypes,
key: unknown,
wrap: typeof toReactive | typeof toReadonly | typeof toShallow
) {
// 拿到proxy的原始对象
target = toRaw(target)
// 通过toRaw拿到key的原始对象
// 如果key和rawKey不相等,则传入的key是一个代理对象
// 那么应该同时将key和rawKey收集为依赖,如果不理解的话,后面添加key的逻辑会再解释
const rawKey = toRaw(key)
if (key !== rawKey) {
track(target, TrackOpTypes.GET, key)
}
track(target, TrackOpTypes.GET, rawKey)
// 从原始对象的原型上拿到原始的has、get方法
const { has, get } = getProto(target)
// 如果key存在,那么返回它的值,如果不存在则继续判断key的原始对象是否存在在target中
// 也就是说当添加一个普通对象作为key值到map上时,不管是key本身还是它的reactive或readonly对象,都可以获取到对应key的值
if (has.call(target, key)) {
return wrap(get.call(target, key))
} else if (has.call(target, rawKey)) {
return wrap(get.call(target, rawKey))
}
}
// 重写Map.prototype.has() / WeakMap.prototype.has() / Set.prototype.has() / WeakSet.prototype.has()
// has方法与get实现差不多,
function has(this: CollectionTypes, key: unknown): boolean {
const target = toRaw(this)
const rawKey = toRaw(key)
if (key !== rawKey) {
track(target, TrackOpTypes.HAS, key)
}
track(target, TrackOpTypes.HAS, rawKey)
const has = getProto(target).has
return has.call(target, key) || has.call(target, rawKey)
}
// 访问属性size执行此方法,注意这里添加依赖的key为ITERATE_KEY
function size(target: IterableCollections) {
target = toRaw(target)
track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.get(getProto(target), 'size', target)
}
// 重写Set.prototype.add() / WeakSet.prototype.add()
// 当通过add添加一个value时,如果value是一个代理对象,那么会先尝试拿到value
// 的原始对象,并判断了value是否存在在set上,如果不存在才派发更新
function add(this: SetTypes, value: unknown) {
value = toRaw(value)
const target = toRaw(this)
const proto = getProto(target)
const hadKey = proto.has.call(target, value)
const result = proto.add.call(target, value)
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, value, value)
}
return result
}
// 重写Map.prototype.set() / WeakMap.prototype.set()
function set(this: MapTypes, key: unknown, value: unknown) {
value = toRaw(value)
const target = toRaw(this)
const { has, get, set } = getProto(target)
// 判断key是否已经存在在map上,先判断传入的key本身是否存在,如果不存在
// 通过toRaw尝试拿到key的原始对象,继续判断
// 也就是说当map上的key是一个对象时,不管是传入key本身或者key的代理对象
// 都可以获取到key的值
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)
const result = set.call(target, key, value)
// 根据是修改key的值还是添加一个新key,传入不同的参数,派发更新
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
return result
}
// 重写Map.prototype.delete / Set.prototype.delete
function deleteEntry(this: CollectionTypes, key: unknown) {
const target = toRaw(this)
const { has, get, delete: del } = getProto(target)
// 判断key是否已经存在在Map或Set上,如果是Map对象则key是将要删除的键值,
// 如果是Set对象则key为将要删除的值
// 先判断传入的key本身是否存在,如果不存在
// 通过toRaw尝试拿到key的原始对象,继续判断
// 也就是说当Map上的key或者Set上的值是一个对象时,不管是传入key本身或者key的代理对象
// 都可以删除这个键或者值
let hadKey = has.call(target, key)
if (!hadKey) {
key = toRaw(key)
hadKey = has.call(target, key)
} else if (__DEV__) {
checkIdentityKeys(target, has, key)
}
// 当原始的get方法不存在时,即target是一个Set对象,那么旧值就是undefinedß
// 如果target是Map对象,则通过原始的get方法拿到旧值
const oldValue = get ? get.call(target, key) : undefined
// 执行原始的delete方法
const result = del.call(target, key)
// 当删除的Map上的key或者Set的值存在时,才需要派发更新
if (hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
}
// 重写Map.prototype.clear / Set.prototype.clear
function clear(this: IterableCollections) {
const target = toRaw(this)
// 判断Map或者Set是否为空
const hadItems = target.size !== 0
// 如果是开发环境则创建一个新的Map或Set传入派发更新的trigger中
const oldTarget = __DEV__
? target instanceof Map
? new Map(target)
: new Set(target)
: undefined
// 调用原始的clear拿到结果
const result = getProto(target).clear.call(target)
// 调用clear时,如果Map或者Set本身不为空,才派发更新
if (hadItems) {
trigger(target, TriggerOpTypes.CLEAR, undefined, undefined, oldTarget)
}
return result
}
// 重写Map.prototype.forEach / Set.prototype.forEach
function createForEach(isReadonly: boolean, shallow: boolean) {
return function forEach(
this: IterableCollections,
callback: Function,
thisArg?: unknown
) {
const observed = this
const target = toRaw(observed)
// 根据传入参数使用不同的wrap,当调用forEach遍历时,访问的item或者key也是一个对象时
// 使用wrap方法将它们转化为只读或者响应式对象
const wrap = isReadonly ? toReadonly : shallow ? toShallow : toReactive
// 如果不是readonly对象,才调用tarck添加依赖
!isReadonly && track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
// important: create sure the callback is
// 1. invoked with the reactive map as `this` and 3rd arg
// 2. the value received should be a corresponding reactive/readonly.
// 调用传入forEach的callback,并将key和value进行只读或者响应式化
function wrappedCallback(value: unknown, key: unknown) {
return callback.call(thisArg, wrap(value), wrap(key), observed)
}
return getProto(target).forEach.call(target, wrappedCallback)
}
}
// 创建对象内部的迭代方法
// method为keys, values, entries, Symbol.iterator中的一种
// 调用这些方法都是返回对象内部的迭代器
function createIterableMethod(
method: string | symbol,
isReadonly: boolean,
shallow: boolean
) {
return function(
this: IterableCollections,
...args: unknown[]
): Iterable & Iterator {
const target = toRaw(this)
// 是否是Map对象
const isMap = target instanceof Map
// 是否返回键值对
const isPair = method === 'entries' || (method === Symbol.iterator && isMap)
// 如果是keys方法并且是Map对象,传入track方法的key为MAP_KEY_ITERATE_KEY
// 后面会解释MAP_KEY_ITERATE_KE和ITERATE_KEY的不同
const isKeyOnly = method === 'keys' && isMap
// 调用原始对象上的方法获得原始的迭代器
const innerIterator = getProto(target)[method].apply(target, args)
// 如果不是readonly对象,才调用tarck添加依赖
const wrap = isReadonly ? toReadonly : shallow ? toShallow : toReactive
!isReadonly &&
track(
target,
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
// 返回重写的迭代器,访问的key或者value也是一个对象时
// 使用wrap方法将它们转化为只读或者响应式对象
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
}
}
}
}
// readonly对象的add、set、delete、clear方法
// 在一个readonly对象上调用这些方法应该发出警告
// 如果调用delete返回false,其他返回对象本身
function createReadonlyMethod(type: TriggerOpTypes): Function {
return function(this: CollectionTypes, ...args: unknown[]) {
if (__DEV__) {
const key = args[0] ? `on key "${args[0]}" ` : ``
console.warn(
`${capitalize(type)} operation ${key}failed: target is readonly.`,
toRaw(this)
)
}
return type === TriggerOpTypes.DELETE ? false : this
}
}
# 类型
// reactive api返回的类型为UnwrapNestedRefs<T>
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
// 如果传入的target的类型T为Ref类型,那么返回值的类型也是Ref,否则将类型T传入UnwrapRef解引用
type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
// 如果类型T是一个Ref那么应该拿到传入这个Ref的类型V,否则还是将T类型传入UnwrapRefSimple
export type UnwrapRef<T> = T extends Ref<infer V>
? UnwrapRefSimple<V>
: UnwrapRefSimple<T>
// 如果T是Function、CollectionTypes、BaseTypes、RefUnwrapBailTypes中的一种,那么直接返回T类型
// RefUnwrapBailTypes是可以让用户自定义的类型
// 如果T是Array,则遍历Array,递归调用UnwrapRefSimple
// 如果T是object,那么将T传入UnwrappedObject中
// 不满足以上情况,都直接返回类型T
type UnwrapRefSimple<T> = T extends
| Function
| CollectionTypes
| BaseTypes
| Ref
| RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
? T
: T extends Array<any>
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
: T extends object ? UnwrappedObject<T> : T
// UnwrappedObject包含了2个部分
// 1、遍历object的所有key,递归调用UnwrapRef
// 2、对象上一些内置Symbol,不会出现在keyof中,所以还需要覆盖一些内置Symbol的类型
type UnwrappedObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } & SymbolExtract<T>
// 当object的上某个属性为以下Symbol值的一种时,先通过infer关键字获取当前Symbol属性的值的类型V
// 然后返回key为Symbol,值为V的类型,否则返回{}
// 例如:
// 当响应式对象为 { [Symbol.match]: () => {}, a: 1, b: '2' } 时
// 传入UnwrappedObject后类型为
// { a: number, b: string } & { [Symbol.match]: () => void }
type SymbolExtract<T> = (T extends { [Symbol.asyncIterator]: infer V }
? { [Symbol.asyncIterator]: V }
: {}) &
(T extends { [Symbol.hasInstance]: infer V }
? { [Symbol.hasInstance]: V }
: {}) &
(T extends { [Symbol.isConcatSpreadable]: infer V }
? { [Symbol.isConcatSpreadable]: V }
: {}) &
(T extends { [Symbol.iterator]: infer V } ? { [Symbol.iterator]: V } : {}) &
(T extends { [Symbol.match]: infer V } ? { [Symbol.match]: V } : {}) &
(T extends { [Symbol.matchAll]: infer V } ? { [Symbol.matchAll]: V } : {}) &
(T extends { [Symbol.replace]: infer V } ? { [Symbol.replace]: V } : {}) &
(T extends { [Symbol.search]: infer V } ? { [Symbol.search]: V } : {}) &
(T extends { [Symbol.species]: infer V } ? { [Symbol.species]: V } : {}) &
(T extends { [Symbol.split]: infer V } ? { [Symbol.split]: V } : {}) &
(T extends { [Symbol.toPrimitive]: infer V }
? { [Symbol.toPrimitive]: V }
: {}) &
(T extends { [Symbol.toStringTag]: infer V }
? { [Symbol.toStringTag]: V }
: {}) &
(T extends { [Symbol.unscopables]: infer V }
? { [Symbol.unscopables]: V }
: {})
# 总结
可以看到Reactivity Api就是创建一个代理对象来拦截一些对原始对象的操作,根据传入的原始对象的类型传入不同的handlers Array、Object传入baseHandlers、Map、Weakmap、Set、Weakset的传入collectionHandlers,其中在handlers中 调用的收集依赖方法track和派发更新方法trigger将在effect章节中分析,这里只需知道在哪些拦截方法中收集依赖或者派发 更新并且了解调用这些方法时传入不同的参数。