baseHandlers详解
在上一篇我们中,我们通过createReactiveObject方法创建并返回了一个proxy对象,这个方法里的一个参数是:baseHandlers。
本文将深入探讨baseHandlers.ts文件中定义的处理逻辑,这些逻辑是Vue 3响应式系统能够实现其强大功能的关键。
1. BaseReactiveHandler: 响应式系统的核心
BaseReactiveHandler是Vue响应式系统中所有处理器的基础,提供了处理普通对象和数组代理行为的基本逻辑。这些处理器利用Proxy来拦截对对象的操作,比如属性访问、设置、枚举和函数调用等,以实现响应式特性。
2. get操作: 依赖追踪与自动解包
当通过代理对象访问属性时,get方法会被触发。这个过程不仅涉及到依赖追踪(track),以便于在属性值变化时通知相关的副作用重新执行,也处理了ref类型的自动解包。此外,对于嵌套对象,Vue会递归地返回其响应式或只读代理,确保整个对象树的响应式完整性。
3. 源代码解析
class BaseReactiveHandler implements ProxyHandler<Target> {
/**
* 构造函数接收两个参数:_isReadonly和_isShallow
* 分别表示代理对象是否为只读和是否为浅响应式
* 这两个属性将影响处理器如何响应对代理对象的操作
*/
constructor(
protected readonly _isReadonly = false,
protected readonly _isShallow = false,
) {}
/**
* get方法是ProxyHandler接口中的一个方法,用来拦截对代理对象属性的读取操作
* 接受三个参数:
* target:代表原始对象,即你通过reactive函数转换成响应式对象之前的那个原始对象。
* key:代表正在被访问的属性名,比如在reactiveObject.a的访问中,key就是"a"。
* receiver:代表代理对象或者继承了代理对象的某个对象,最常见的情况是代理对象本身。
*/
get(target: Target, key: string | symbol, receiver: object) {
const isReadonly = this._isReadonly,
isShallow = this._isShallow;
/**
* 特殊标志属性处理
* 当访问的是Vue内部定义的特殊标志(ReactiveFlags)时,根据代理对象的状态返回相应的布尔值
*/
if (key === ReactiveFlags.IS_REACTIVE) {
//检查是否为响应式:如果当前代理对象是响应式的,返回!isReadonly
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
//检查是否为只读:如果当前代理对象是只读的,返回isReadonly
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
//检查是否为浅层响应式:如果访问的是浅层响应式,返回isShallow
return isShallow
} else if (key === ReactiveFlags.RAW) {
//获取原始对象:当访问ReactiveFlags.RAW时,意图是获取代理对象背后的原始对象
if (
//判断receiver是否等于代理映射中的target
receiver ===
(isReadonly
? isShallow
? shallowReadonlyMap
: readonlyMap
: isShallow
? shallowReactiveMap
: reactiveMap
).get(target) ||
/**
* 检查target和receiver是否有相同的原型:
* 如果它们的原型相同,这通常意味着receiver是对target的一个用户定义的代理,或者两者通过某种方式相关联,但本质上仍然代表同一个对象或对象结构。
* 在这种情况下,即使receiver不直接是响应式系统内部维护的代理映射中的对象,也应该将对ReactiveFlags.RAW属性的访问视为对原始对象的合法访问,并返回target。
*/
Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
) {
return target
}
return
}
/**
* 其他情况:普通属性访问逻辑
* 当访问的不是这些内部状态标志时,就会按照普通的属性访问来处理,进行如依赖追踪(track)和解包ref值等操作
*/
//当访问的键是一个数组操作或hasOwnProperty方法时,可能会通过arrayInstrumentations进行特殊处理,以确保响应式系统的正确行为
const targetIsArray = isArray(target)
if (!isReadonly) {
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
if (key === 'hasOwnProperty') {
return hasOwnProperty
}
}
//获取属性值:通过ES6的Reflect API,更安全、更规范地进行元编程操作,如属性访问
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 (isShallow) {
return res
}
//自动解包ref:如果访问的属性值是一个ref,并且不是数组的索引访问,会自动解包ref的值
if (isRef(res)) {
return targetIsArray && isIntegerKey(key) ? res : res.value
}
//递归响应式处理:如果属性值是一个对象,并且不是只读的且不是浅层响应式的,则会递归地将该对象也转换为响应式或只读
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
4. 源码流程图
5. 示例:reactiveObject.a 获取属性时发生了什么?
const original = { a: 1 };
const reactiveObject = reactive(original);
console.log(reactiveObject.a);// 输出:1
5.1 Proxy代理拦截
当尝试访问reactiveObject.a时,Proxy拦截器捕获到这一操作,并调用相应的get方法。
5.2 调用get方法
Proxy根据配置的处理器(例如BaseReactiveHandler),调用get方法来处理这次访问,传入的参数分别是原始对象(target),属性名(“a”,即key),和代理对象本身(receiver)。
5.3 get方法执行
get方法根据这些参数执行相应的逻辑,如检查访问的是否是特殊标志属性、执行依赖追踪、返回属性值等:
5.3.1 响应式标志检查
首先检查是否访问的是特殊的响应式标志属性:不是
5.3.2 a属性是一个普通值
使用Reflect.get从原始对象中获取请求的属性值 :1
5.3.3依赖追踪(track)
尽管a不是ref,但是访问reactiveObject.a时,Vue的响应式系统仍然会对这次属性访问进行依赖追踪(通过track函数)
5.3.4 返回属性值1
6. 总结
通俗点来说,这里定义了我们在vue里,对一个对象的属性访问时,vue内部会执行的逻辑。在下一篇,我们将继续学习继承BaseReactiveHandler的MutableReactiveHandler是如何实现set、has等更多方法。