reactive
reactive返回一个对象的响应式代理 源码位置: packages -> reactivity -> src -> reactive.ts
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
)
}
判断对象传入的是不是reactive对象,如果是就直接retrn出去,如果不是就创建ReactiveObject
- mutableHandlers 用于处理普通对象(即非集合类型的对象) 源码位置:reactivity -> src -> baseHandlers.ts
- mutableCollectionHandlers 专门用于处理集合类型(如数组、Map、Set 等)的处理器, 源码位置:reactivity -> src -> collectionHandlers
mutableHandlers 普通对象拦截处理
const get = /*#__PURE__*/ createGetter()
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
get拦截操作解读
createGetter 返回一个get拦截函数
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (
key === ReactiveFlags.RAW &&
receiver === (isReadonly ? readonlyMap : reactiveMap).get(target)
) {
return target
}
const targetIsArray = isArray(target)
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
const res = Reflect.get(target, key, receiver)
const keyIsSymbol = isSymbol(key)
if (
keyIsSymbol ? builtInSymbols.has(key as symbol)
: key === `__proto__` || key === `__v_isRef`
) {
return res
}
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
if (shallow) {
return res
}
if (isRef(res)) {
// ref unwrapping - does not apply for Array + integer key.
const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
return shouldUnwrap ? res.value : res
}
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
}
}
一:检查特殊键的处理
ReactiveFlags.IS_REACTIVE: 返回对象是否为响应式(非只读)。ReactiveFlags.IS_READONLY: 返回对象是否为只读。ReactiveFlags.RAW: 返回原始目标对象(未代理)。 首先会判断读取的属性是不是这三种类型,如果是就返回对应的处理。
二: 数组特殊处理
接下来处理,判断当前的target对象是不是数组,
arrayInstrumentations
const arrayInstrumentations: Record<string, Function> = {}
// instrument identity-sensitive Array methods to account for possible reactive
// values
;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
const method = Array.prototype[key] as any
arrayInstrumentations[key] = function(this: unknown[], ...args: unknown[]) {
const arr = toRaw(this)
for (let i = 0, l = this.length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '')
}
// we run the method using the original args first (which may be reactive)
const res = method.apply(arr, args)
if (res === -1 || res === false) {
// if that didn't work, run it again using raw values.
return method.apply(arr, args.map(toRaw))
} else {
return res
}
}
})
// instrument length-altering mutation methods to avoid length being tracked
// which leads to infinite loops in some cases (#2137)
;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
const method = Array.prototype[key] as any
arrayInstrumentations[key] = function(this: unknown[], ...args: unknown[]) {
pauseTracking()
const res = method.apply(this, args)
enableTracking()
return res
}
})
// 用于暂时暂停和恢复依赖追踪,代码位置: packages -> reactivity -> src -> effect.ts
let shouldTrack = true
const trackStack: boolean[] = []
export function pauseTracking() {
trackStack.push(shouldTrack)
shouldTrack = false
}
export function enableTracking() {
trackStack.push(shouldTrack)
shouldTrack = true
}
通过为数组的特定方法创建增强版本,Vue3的响应式系统能够更精确地追踪数据的变化,并避免潜在的无限循环问题。这些仪器化方法是Vue响应式系统的核心部分,它们使得Vue能够高效地处理数据绑定和视图更新。
身份敏感的数组方法
track函数是Vue响应式系统的一部分,用于追踪依赖关系。在这里,它遍历数组的每个元素,并追踪对这些元素的访问(使用元素的索引作为键)。- 首先尝试使用原始参数调用原始数组方法。如果方法返回
-1或false(表示未找到),则再次调用该方法,但这次使用toRaw处理过的参数(确保参数是原始值而不是响应式对象)。这是因为,在某些情况下,响应式包装器可能会干扰查找操作的结果。
改变数组长度的突变方法
pauseTracking和enableTracking是Vue响应式系统的函数,用于暂时暂停和恢复依赖追踪。这是必要的,因为直接修改数组长度可能会导致无限循环的依赖追踪(例如,当数组长度变化触发观察者更新,而这些更新又进一步修改数组长度时)。- 仪器化方法在执行原始数组方法之前调用
pauseTracking,然后在方法执行完成后调用enableTracking。这确保了数组长度的变化不会导致不必要的依赖追踪。
三:访问属性
const res = Reflect.get(target, key, receiver)
const keyIsSymbol = isSymbol(key)
if (
keyIsSymbol ? builtInSymbols.has(key as symbol)
: key === `__proto__` || key === `__v_isRef`
) {
return res
}
- 通过Reflect.get获取值
- 对一些特殊符号键(内置符号或
__proto__、__v_isRef)直接返回结果,不进行额外处理。
四:收集依赖副作用 & 浅响应式处理
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
if (shallow) {
return res
}
- 如果不是只读对象,则调用
track函数进行依赖收集。 -如果设置了shallow,则直接返回属性值,不进行深度响应式转换。
五: ref解包
// 源码位置: packages -> shared -> src -> index.ts
export const isIntegerKey = (key: unknown) =>
isString(key) &&
key !== 'NaN' &&
key[0] !== '-' &&
'' + parseInt(key, 10) === key
if (isRef(res)) {
// ref unwrapping - does not apply for Array + integer key.
const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
return shouldUnwrap ? res.value : res
}
- 如果属性值是
ref对象(Vue的响应式引用),则根据条件解包返回其value属性。对于数组和整数键,不解包以保持数组元素的行为一致性。
六:深度响应式转换
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)
}
- 如果属性值是对象,则根据
isReadonly参数,使用readonly或reactive函数进行深度响应式转换。
set拦截操作解读
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
const oldValue = (target as any)[key]
if (!shallow) {
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
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
}
}
先获取保存旧值,然后非浅模式处理
非浅模式处理
value = toRaw(value)
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
首先尝试将新值转换为原始值(如果它是一个响应式引用,则获取其内部值)。如果目标不是数组,旧值是一个响应式引用,而新值不是响应式引用,那么直接将新值赋给旧值的.value属性,并返回true。
- 在浅模式下,不执行上述转换,对象按原样设置,不考虑其是否响应式。
检查键是否存在
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
判断目标对象是否已经拥有这个键。对于数组,如果键是一个整数索引,并且索引小于数组长度,则认为键存在;对于对象,使用hasOwn函数检查键是否直接存在于对象上。
使用Reflect.set设置值 触发依赖更新
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) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
如果目标对象与接收者的原始对象相同(确保不是通过原型链继承的某个对象),则根据键是否存在以及值是否改变,触发相应的依赖更新。如果键不存在,则触发添加操作(ADD);如果键存在且值已改变,则触发设置操作(SET)。
最后,返回Reflect.set的结果,这通常是true,表示设置操作成功。
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)
if (result && hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
}
首先检查key是否存在,,然后获取key对应的值,再通过Reflect.deleteProperty删除属性。最后如果删除成功且key存在就触发依赖更新
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
}
has 函数用于检查对象 target 上是否存在键 key
条件性依赖收集:
if (!isSymbol(key) || !builtInSymbols.has(key)):这个条件检查key是否不是符号(Symbol),或者即使它是符号,也不是内置符号集合builtInSymbols中的一员。- 如果条件为真,则调用
track(target, TrackOpTypes.HAS, key)。track函数是Vue 3响应式系统的一部分,用于收集依赖关系。在这里,它表示有一个依赖正在检查target对象上是否存在key属性。 - 注意:对于某些内置符号(如
Symbol.iterator),Vue可能选择不收集依赖,因为它们通常用于标准的迭代行为,而不是用于响应式数据的直接访问。
ownKeys函数
function ownKeys(target: object): (string | number | symbol)[] {
track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.ownKeys(target)
}
ownKeys 函数用于获取对象 target 的所有键,包括可枚举和不可枚举属性以及符号键。
依赖收集
track(target, TrackOpTypes.ITERATE, ITERATE_KEY):在迭代对象之前,调用track函数来收集依赖关系。这里,TrackOpTypes.ITERATE表示操作类型是迭代,而ITERATE_KEY是一个特殊的键或标识符,用于表示这个依赖是来自于对对象键的迭代。
获取所有键:
Reflect.ownKeys(target) 返回一个数组,包含 target 对象的所有键,包括可枚举和不可枚举属性以及符号键。
mutableCollectionHandlers 处理集合类型( Map、Set 等)。
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: createInstrumentationGetter(false, false)
}
function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
const instrumentations = shallow ? 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
)
}
}
// 源码位置: packages -> reactivity -> src -> index.ts
export const hasOwn = (
val: object,
key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)
isReadonly: 一个布尔值,指示创建的响应式对象是否为只读shallow: 一个布尔值,指示是否创建浅响应式对象- shallowInstrumentations: 处理浅响应式
- readonlyInstrumentations: 处理只读响应式的逻辑
- mutableInstrumentations: 处理可变响应式的逻辑
- hasOwn 该函数用于检查一个对象(
val)是否自身(而非通过其原型链)拥有一个特定的属性(key)
createReactiveObject
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<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 proxyMap = isReadonly ? readonlyMap : reactiveMap
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only a whitelist of 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
}
- 首先判断传入target是不是对象类型,如果不是直接return出去,在开发环境会打印提示log
- 然后判断传入target是不是响应式,
- 根据isReadonly判断获取响应代理集合
- 如果传入target已经在存在代理,就直接return出已经存在的代理
- 如果target的类型事白名单的直接return出去
- 创建Proxy代理,并将创建的代理存入响应代理集合中
- 最后return出去Proxy代理