响应式系统(浅析)
核心模块
Reactive API: 创建响应式对象(如 reactive、ref、computed)
依赖搜集系统:追踪属性访问与副作用函数的依赖关系(track trigger)
副作用函数:包裹响应式操作的函数(effect)
/**
* @description: reactive 函数 reactive响应的起点函数
* @param {object} target
* @returns {*}
*/
export function reactive(target: object) {
console.log("初始位置");
debugger;
// if trying to observe a readonly proxy, return the readonly version.
// 如果目标对象是只读的,则直接返回目标对象
if (isReadonly(target)) {
return target;
}
// 创建响应式对象
return createReactiveObject(
target,
false,
mutableHandlers, // 创建响应式对象的处理器
mutableCollectionHandlers, // 创建响应式对象的集合处理器
reactiveMap // 代理对象
);
}function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
/**
* 如果target不是对象,则直接返回target
*/
if (!isObject(target)) {
return target;
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
/**
* 如果target是proxy,则直接返回target
*/
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target;
}
// only specific value types can be observed.
// 获取目标对象的类型
const targetType = getTargetType(target);
// 如果目标对象的类型是无效的,则直接返回目标对象
if (targetType === TargetType.INVALID) {
return target;
}
/**
* 如果target已经存在对应的Proxy,则直接返回
*/
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
// collectionHandlers
// baseHandlers
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
);
// 将代理对象设置到代理对象映射表中
proxyMap.set(target, proxy);
// 返回代理对象
return proxy;
}/**
* 可变响应式处理器,继承自 BaseReactiveHandler
* 用于处理可变的响应式对象,支持属性的设置、删除等操作
*/
class MutableReactiveHandler extends BaseReactiveHandler {
constructor(isShallow = false) {
super(false, isShallow);
console.log("MutableReactiveHandler", isShallow);
}
/**
* Proxy 的 set 函数,当设置响应式对象的属性时会被调用
* 这是响应式系统中触发更新的核心方法
* @param target 原始目标对象
* @param key 要设置的属性键
* @param value 要设置的新值
* @param receiver Proxy 对象本身
* @returns 是否设置成功
*/
set(
target: Record<string | symbol, unknown>,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
debugger;
// 获取目标对象上该属性的旧值,用于后续的比较和触发更新。
let oldValue = target[key];
// 非浅响应式模式下的特殊处理
if (!this._isShallow) {
const isOldValueReadonly = isReadonly(oldValue);
// value 不是浅响应式,且不是只读的
if (!isShallow(value) && !isReadonly(value)) {
// value 和 oldValue 转换为原始对象
oldValue = toRaw(oldValue);
value = toRaw(value);
}
// 处理 ref 的特殊情况:如果旧值是 ref 而新值不是 ref
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
if (isOldValueReadonly) {
return false;
} else {
// 直接修改 ref 的 value,而不是替换整个 ref
oldValue.value = value;
return true;
}
}
} else {
// 在浅模式下,对象按原样设置,无论是否为响应式
}
// 判断目标对象上是否存在该属性
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length // 数组的整数键:检查索引是否在长度范围内
: hasOwn(target, key); // 普通对象:检查属性是否存在
// 使用 Reflect.set 实际设置属性值
const result = Reflect.set(
target,
key,
value,
isRef(target) ? target : receiver
);
// 只有当 target 是 receiver 的原始对象时才触发更新
// 这避免了在原型链上的对象触发不必要的更新
if (target === toRaw(receiver)) {
if (!hadKey) {
// 新增属性:触发 ADD 操作
trigger(target, TriggerOpTypes.ADD, key, value);
} else if (hasChanged(value, oldValue)) {
// 修改属性且值发生变化:触发 SET 操作
trigger(target, TriggerOpTypes.SET, key, value, oldValue);
}
}
// 返回设置结果
return result;
}
}// 基类,所有响应式处理器的基类 new Proxy(target, handler) 中的 handler
class BaseReactiveHandler implements ProxyHandler<Target> {
constructor(
protected readonly _isReadonly = false, // 是否为只读模式
protected readonly _isShallow = false, // 是否为浅响应式模式
) {}
/**
* Proxy 的 get 陷阱函数,当访问响应式对象的属性时会被调用
* @param target 原始目标对象
* @param key 要访问的属性键
* @param receiver Proxy 对象本身
* @returns 属性值
*/
get(target: Target, key: string | symbol, receiver: object): any {
// 处理特殊标志位,用于跳过响应式处理
if (key === ReactiveFlags.SKIP) return target[ReactiveFlags.SKIP]
const isReadonly = this._isReadonly,
isShallow = this._isShallow
// 处理响应式标志位查询
if (key === ReactiveFlags.IS_REACTIVE) {
// 查询对象是否为响应式:只读对象不算响应式
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
// 查询对象是否为只读
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
// 查询对象是否为浅响应式
return isShallow
} else if (key === ReactiveFlags.RAW) {
// 获取原始对象,绕过代理
if (
receiver ===
(isReadonly
? isShallow
? shallowReadonlyMap
: readonlyMap
: isShallow
? shallowReactiveMap
: reactiveMap
).get(target) ||
// receiver 不是响应式代理,但有相同的原型
// 这意味着 receiver 是响应式代理的用户代理
Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
) {
return target
}
// 提前返回 undefined
return
}
// 判断目标对象是否为数组
const targetIsArray = isArray(target)
// 非只读模式下处理特殊逻辑
if (!isReadonly) {
let fn: Function | undefined
// 如果是数组且存在数组方法重写,返回重写的方法
if (targetIsArray && (fn = arrayInstrumentations[key])) {
return fn
}
// 处理 hasOwnProperty 方法,确保依赖收集
if (key === 'hasOwnProperty') {
return hasOwnProperty
}
}
// 使用 Reflect.get 获取属性值 核心
const res = Reflect.get(
target,
key,
// 如果这是一个包装 ref 的代理,使用原始 ref 作为 receiver
// 这样我们就不需要在所有类方法中调用 `toRaw` 了
isRef(target) ? target : receiver,
)
// 对于内置 Symbol 和非可追踪的键,直接返回结果,不进行依赖收集
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
// 非只读模式下进行依赖收集(响应式系统的核心)
if (!isReadonly) {
// 依赖收集 核心代码
track(target, TrackOpTypes.GET, key)
}
// 浅响应式模式直接返回结果,不进行深层转换
if (isShallow) {
return res
}
// 处理 ref 的解包
if (isRef(res)) {
// ref 解包 - 对于数组 + 整数键跳过解包
return targetIsArray && isIntegerKey(key) ? res : res.value
}
// 如果返回值是对象,将其转换为响应式对象
if (isObject(res)) {
// 将返回值也转换为代理。我们在这里进行 isObject 检查
// 以避免无效值警告。还需要延迟访问 readonly
// 和 reactive 以避免循环依赖。
return isReadonly ? readonly(res) : reactive(res)
}
// 返回原始值
return res
}
}依赖搜集系统(track trigger)
track
/**
* 追踪对响应式属性的访问
*
* 这将检查当前正在运行的effect,并将其记录为dep
* dep记录所有依赖于该响应式属性的effects
*
* @param target - 持有响应式属性的对象
* @param type - 定义对响应式属性的访问类型
* @param key - 要追踪的响应式属性的标识符
*/
export function track(target: object, type: TrackOpTypes, key: unknown): void {
// shouldTrack 表示是否应该进行依赖收集
// activeSub 表示当前正在执行的副作用函数(effect)
if (shouldTrack && activeSub) {
// 获取目标对象的依赖映射
let depsMap = targetMap.get(target);
// 如果targetMap中没有target对应的depsMap,则创建一个
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
// 获取属性的依赖集合
let dep = depsMap.get(key);
// 如果depsMap中没有key对应的dep,则创建一个
if (!dep) {
depsMap.set(key, (dep = new Dep()));
dep.map = depsMap;
dep.key = key;
}
// 调用dep的track方法进行依赖收集
if (__DEV__) {
dep.track({
target,
type,
key,
});
} else {
dep.track();
}
}
}// dep.track()
/**
* 追踪依赖的核心方法
*
* 参数说明:
* - activeSub:当前正在执行的副作用函数(effect)
* - shouldTrack:是否应该进行依赖收集
* - activeSub === this.computed:避免计算属性自己收集自己作为依赖
*/
track(debugInfo?: DebuggerEventExtraInfo): Link | undefined {
// 条件检查:如果没有活跃的effect,或者不应该追踪,或者当前effect是computed,则返回
if (!activeSub || !shouldTrack || activeSub === this.computed) {
return
}
// 获取或创建Link连接
// this.activeLink 表示当前活跃的effect与该dep之间的链接
let link = this.activeLink
// 如果link不存在,或者link的sub不等于activeSub,则为当前activeSub创建一个新的Link连接
if (link === undefined || link.sub !== activeSub) {
// 创建新的Link连接
link = this.activeLink = new Link(activeSub, this)
/**
* 建立副作用函数到依赖的双向链表:
* 每个副作用函数维护一个deps链表,记录它依赖的所有响应式数据
* 新创建的link被添加到链表尾部
*/
if (!activeSub.deps) {
// 如果当前执行的副作用函数没有deps链表,则将link作为deps的头部 & 尾部
activeSub.deps = activeSub.depsTail = link
} else {
// 将link添加到deps链表尾部
link.prevDep = activeSub.depsTail
activeSub.depsTail!.nextDep = link
activeSub.depsTail = link
}
// 调用addSub函数,将link添加到当前dep的订阅者列表中
addSub(link) // ****************
} else if (link.version === -1) {
// 重用上次运行的链接 - 已经是订阅者,只需同步版本
link.version = this.version
/**
* 如果这个dep有next,说明它不在尾部 - 将其移动到尾部
* 这确保了effect的dep列表按照在求值期间访问的顺序排列
*/
if (link.nextDep) {
const next = link.nextDep
next.prevDep = link.prevDep
if (link.prevDep) {
link.prevDep.nextDep = next
}
// 将link移动到尾部
link.prevDep = activeSub.depsTail
link.nextDep = undefined
activeSub.depsTail!.nextDep = link
activeSub.depsTail = link
// 如果这是头部,指向新的头部
if (activeSub.deps === link) {
activeSub.deps = next
}
}
}
return link
}/**
* 添加订阅者的核心函数
* 建立dep到effect的双向链表
*/
function addSub(link: Link) {
// 增加订阅者计数器
link.dep.sc++;
// 检查是否正在追踪
if (link.sub.flags & EffectFlags.TRACKING) {
// 处理计算属性的特殊情况
const computed = link.dep.computed;
// 计算属性第一次获得订阅者时,启用追踪并订阅其所有依赖
if (computed && !link.dep.subs) {
computed.flags |= EffectFlags.TRACKING | EffectFlags.DIRTY;
for (let l = computed.deps; l; l = l.nextDep) {
addSub(l);
}
}
// 建立dep到effect的双向链表
const currentTail = link.dep.subs;
if (currentTail !== link) {
link.prevSub = currentTail;
if (currentTail) currentTail.nextSub = link;
}
// 开发环境下设置subsHead
if (__DEV__ && link.dep.subsHead === undefined) {
link.dep.subsHead = link;
}
// 更新dep的订阅者列表
link.dep.subs = link;
}
}trigger
/**
* 查找与目标(或特定属性)关联的所有deps,并触发其中存储的effects
*
* @param target - 响应式对象
* @param type - 定义需要触发effects的操作类型
* @param key - 可用于定位目标对象中的特定响应式属性
* @param newValue - 新值
* @param oldValue - 旧值
* @param oldTarget - 旧的目标对象(用于Map/Set)
*/
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
): void {
debugger;
const depsMap = targetMap.get(target);
if (!depsMap) {
// 从未被追踪过
globalVersion++;
return;
}
/**
* 运行dep的触发器函数
*/
const run = (dep: Dep | undefined) => {
if (dep) {
if (__DEV__) {
dep.trigger({
target,
type,
key,
newValue,
oldValue,
oldTarget,
});
} else {
dep.trigger();
}
}
};
startBatch();
if (type === TriggerOpTypes.CLEAR) {
// 集合被清空,触发目标的所有effects
depsMap.forEach(run);
} else {
const targetIsArray = isArray(target);
const isArrayIndex = targetIsArray && isIntegerKey(key);
if (targetIsArray && key === "length") {
// 数组长度变化时的特殊处理
const newLength = Number(newValue);
depsMap.forEach((dep, key) => {
if (
key === "length" ||
key === ARRAY_ITERATE_KEY ||
(!isSymbol(key) && key >= newLength)
) {
run(dep);
}
});
} else {
// 为SET | ADD | DELETE操作调度运行
if (key !== void 0 || depsMap.has(void 0)) {
run(depsMap.get(key));
}
// 为任何数字键变化调度ARRAY_ITERATE(length在上面处理)
if (isArrayIndex) {
run(depsMap.get(ARRAY_ITERATE_KEY));
}
// 也为ADD | DELETE | Map.SET的迭代键运行
switch (type) {
case TriggerOpTypes.ADD:
if (!targetIsArray) {
// 对象添加属性
run(depsMap.get(ITERATE_KEY));
if (isMap(target)) {
run(depsMap.get(MAP_KEY_ITERATE_KEY));
}
} else if (isArrayIndex) {
// 数组添加新索引 -> 长度变化
run(depsMap.get("length"));
}
break;
case TriggerOpTypes.DELETE:
if (!targetIsArray) {
// 对象删除属性
run(depsMap.get(ITERATE_KEY));
if (isMap(target)) {
run(depsMap.get(MAP_KEY_ITERATE_KEY));
}
}
break;
case TriggerOpTypes.SET:
if (isMap(target)) {
// Map设置值
run(depsMap.get(ITERATE_KEY));
}
break;
}
}
}
endBatch();
}/**
* 触发更新:增加版本号并通知所有订阅者
*/
trigger(debugInfo?: DebuggerEventExtraInfo): void {
this.version++ // 增加dep的版本号
globalVersion++ // 增加全局版本号
this.notify(debugInfo) // 通知所有订阅者
}
/**
* 通知所有订阅者执行更新
*/
notify(debugInfo?: DebuggerEventExtraInfo): void {
startBatch() // 开始批处理
try {
if (__DEV__) {
// 在开发环境下,按原始顺序调用onTrigger钩子
for (let head = this.subsHead; head; head = head.nextSub) {
if (head.sub.onTrigger && !(head.sub.flags & EffectFlags.NOTIFIED)) {
head.sub.onTrigger(
extend(
{
effect: head.sub,
},
debugInfo,
),
)
}
}
}
// 遍历所有订阅者,触发更新
for (let link = this.subs; link; link = link.prevSub) {
if (link.sub.notify()) {
// 如果notify()返回true,说明这是一个computed
// 同时调用其dep的notify,在这里调用而不是在computed的notify内部调用
// 是为了减少调用栈深度
;(link.sub as ComputedRefImpl).dep.notify()
}
}
} finally {
endBatch() // 结束批处理
}
}/**
* 结束批处理,执行所有批处理的副作用
* @internal
*/
export function endBatch(): void {
if (--batchDepth > 0) {
return
}
// 先处理计算属性
if (batchedComputed) {
let e: Subscriber | undefined = batchedComputed
batchedComputed = undefined
while (e) {
const next: Subscriber | undefined = e.next
e.next = undefined
e.flags &= ~EffectFlags.NOTIFIED
e = next
}
}
// 处理普通副作用
let error: unknown
while (batchedSub) {
let e: Subscriber | undefined = batchedSub
batchedSub = undefined
while (e) {
const next: Subscriber | undefined = e.next
e.next = undefined
e.flags &= ~EffectFlags.NOTIFIED
if (e.flags & EffectFlags.ACTIVE) {
try {
// ACTIVE 标志仅用于副作用
;(e as ReactiveEffect).trigger() // ******
} catch (err) {
if (!error) error = err
}
}
e = next
}
}
}
/**
* 触发副作用
* 根据状态决定如何执行
*/
trigger(): void {
if (this.flags & EffectFlags.PAUSED) {
// 如果暂停,加入暂停队列
pausedQueueEffects.add(this)
} else if (this.scheduler) {
// 如果有自定义调度器,使用调度器
this.scheduler()
} else {
// 否则直接运行
this.runIfDirty() // this.run()
}
}/**
* 运行副作用函数
* 这是副作用执行的核心方法
*/
run(): T {
// TODO cleanupEffect
// 如果副作用已停止,直接执行函数
if (!(this.flags & EffectFlags.ACTIVE)) {
// stopped during cleanup
return this.fn()
}
// 设置运行标志
this.flags |= EffectFlags.RUNNING
// 清理之前的副作用
cleanupEffect(this)
// 准备依赖追踪
prepareDeps(this)
// 保存当前活跃副作用
const prevEffect = activeSub
const prevShouldTrack = shouldTrack
activeSub = this
shouldTrack = true
try {
// 执行副作用函数
return this.fn() //指向被包裹的函数
} finally {
// 开发环境检查
if (__DEV__ && activeSub !== this) {
warn(
'Active effect was not restored correctly - ' +
'this is likely a Vue internal bug.',
)
}
// 清理依赖
cleanupDeps(this)
// 恢复之前的活跃副作用
activeSub = prevEffect
shouldTrack = prevShouldTrack
// 清除运行标志
this.flags &= ~EffectFlags.RUNNING
}
}副作用函数(effect)
- Vue 3 的 watch 和 watchEffect 内部都使用 effect
- 组件的渲染函数会被包装成 effect
- 计算属性 computed 也基于 effect 实现
- 用户可以直接使用 effect 来创建自定义的响应式副作用
/**
* 创建副作用函数
*
* Vue 3 的 watch 和 watchEffect 内部都使用 effect
* 组件的渲染函数会被包装成 effect
* 计算属性 computed 也基于 effect 实现
* 用户可以直接使用 effect 来创建自定义的响应式副作用
*/
export function effect<T = any>(
fn: () => T,
options?: ReactiveEffectOptions
): ReactiveEffectRunner<T> {
// 如果传入的已经是运行器,提取其副作用函数
if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect) {
fn = (fn as ReactiveEffectRunner).effect.fn;
// fn就是最后执行的副作用
}
// 创建副作用函数
const e = new ReactiveEffect(fn);
if (options) {
extend(e, options);
}
try {
e.run();
} catch (err) {
e.stop();
throw err;
}
const runner = e.run.bind(e) as ReactiveEffectRunner;
runner.effect = e;
return runner;
}通过测试文件断点调试
<script src="../dist/vue.global.js"></script>
<div id="demo">
<h3>{{count.value}}</h3>
<button @click="addFn">add+1</button>
<button @click="reduceFn">reduce-1</button>
</div>
<script>
const { createApp, ref, reactive, watchEffect } = Vue
debugger
let app = createApp({
setup() {
const count = reactive({
value: 0
})
const addFn = () => {
debugger
count.value++
}
const reduceFn = () => {
count.value--
}
return {
count,
addFn,
reduceFn
}
},
})
debugger
app.mount('#demo')
</script>
<style>
</style>断点执行流程(部分)
初始化阶段
点击更新数据阶段
触发 addFn 函数
触发 BaseReactiveHandler 中的 get 方法 (暂时先关注执行的代码)
Reflect.get 的第三个参数决定了属性访问器 get 函数的 this 指向
// 基类,所有响应式处理器的基类 new Proxy(target, handler) 中的 handler
// receiver 发起属性访问的对象 通常是proxy本身
get(target: Target, key: string | symbol, receiver: object): any {
// 是不是只读
const isReadonly = this._isReadonly,
isShallow = this._isShallow
// Reflect.get 读取target的key属性
const res = Reflect.get(
target,
key,
// if this is a proxy wrapping a ref, return methods using the raw ref
// as receiver so that we don't have to call `toRaw` on the ref in all
// its class methods
isRef(target) ? target : receiver,
// 若target已经是ref代理对象了,则指向target 否则this指向receiver
)
// 非只读
if (!isReadonly) {
// 依赖收集 核心代码
track(target, TrackOpTypes.GET, key) // ***************
}
return res
}问题 1: 为什么要用 Reflect.get
this
在访问对象的 getter 属性 或 原型链上的属性 时,this 指向会影响依赖收集的正确性。
举个例子:
const obj = {
name: "Vue",
get fullName() {
// getter 中访问了 this.name
return this.name + " 3";
},
};
const proxy = new Proxy(obj, {
get(target, property, receiver) {
// 依赖收集:记录当前副作用函数与 property 的关联
track(target, property);
// 错误写法:直接返回 target[property]
return target[property];
},
});当访问 proxy.fullName 时:
target[property]会执行obj.fullNamegetter,此时 getter 中的this指向 原始对象** ****obj\*\*(而非代理proxy)。- 因此,
getter中访问的this.name会直接读取obj.name,不会经过****proxy****的****get****拦截器,导致name属性的依赖无法被收集。 - 后续若修改
proxy.name,fullName依赖的name变化不会触发更新,响应式失效。
Reflect.get(target, property, receiver) 的第三个参数 receiver 可以指定 getter 执行时的 this 指向。
在 Vue 3 的响应式中,receiver 就是当前的代理对象(proxy)。修改上面的代码:
const proxy = new Proxy(obj, {
get(target, property, receiver) {
track(target, property);
// 正确写法:用 Reflect.get 并传入 receiver
return Reflect.get(target, property, receiver);
},
});此时访问 proxy.fullName:
Reflect.get会让 getter 中的this指向receiver(即proxy)。- 因此,
getter中访问的this.name会触发proxy.name的get拦截,进而被track收集依赖。 - 后续修改
proxy.name时,fullName的依赖会被正确触发更新。
如果对象的属性来自 原型链,直接用 target[property] 会跳过代理的拦截逻辑,导致依赖收集遗漏。
举个例子:
// 原型对象
const proto = {
age: 18,
};
// 原始对象,继承自 proto
const obj = Object.create(proto);
// 代理 obj
const proxy = new Proxy(obj, {
get(target, property, receiver) {
track(target, property);
return target[property]; // 错误写法
},
});当访问 proxy.age 时:
obj本身没有age属性,target[property]会直接去原型链proto上找age,但proto没有被代理,因此age的访问 不会经过****proxy****的****get****拦截,导致age的依赖无法收集。
Reflect.get 会严格按照 对象的原型链查找规则 执行,且在查找过程中若遇到代理对象,会触发其 get 拦截器。
修改代码后:
get(target, property, receiver) {
track(target, property);
return Reflect.get(target, property, receiver);
}此时访问 proxy.age:
Reflect.get会先查obj(无age),再查原型proto。但由于receiver是proxy,原型链上的属性访问也会通过proxy的拦截器,确保依赖被正确收集。
当代理对象被 继承 时,receiver 可能是继承了代理的子对象,而非代理本身。Reflect.get 能保证这种场景下的 this 指向正确。
例如:
const proxy = new Proxy(
{ name: "Vue" },
{
get(target, property, receiver) {
track(target, property);
return Reflect.get(target, property, receiver);
},
}
);
// 子对象继承自 proxy
const child = Object.create(proxy);
console.log(child.name); // 访问子对象的 name 属性此时 child.name 会触发 proxy 的 get 拦截,receiver 是 child,Reflect.get 会正确处理继承关系,确保 name 的依赖收集到 proxy 上,后续修改 proxy.name 时,child 的依赖也会更新。
问题 2: receiver 的作用
保持 getter 的this 绑定如果直接返回 target[property],当属性是 getter 时,getter 内部的 this 会指向原始对象 target,而非代理对象 proxy。这可能导致:
- 无法触发代理的其他拦截器(如
set)。 - 在响应式系统(如 Vue 3)中失效。
正确做法:使用 Reflect.get(target, property, receiver) 转发属性访问,确保 this 指向 proxy。
当代理对象被继承时,receiver 会是继承自 proxy 的对象,而非 proxy 本身
触发 track 方法,但是并未执行内部逻辑,activeSub 是 undefined
/**
* Tracks access to a reactive property.
*
* This will check which effect is running at the moment and record it as dep
* which records all effects that depend on the reactive property.
*
* 将检查当前正在运行中的effect,并记录为dep,dep记录所有依赖于该响应式属性的effect
*
* @param target - Object holding the reactive property. 响应式对象
* @param type - Defines the type of access to the reactive property. 访问类型
* @param key - Identifier of the reactive property to track. 响应式属性
*/
// target > depsMap > dep
export function track(target: object, type: TrackOpTypes, key: unknown): void {
if (shouldTrack && activeSub) {
// fasle
/***/
}
}触发 MutableReactiveHandler 中的 set 方法

set(
target: Record<string | symbol, unknown>,
key: string | symbol,
value: unknown,
receiver: object,
): boolean {
// 获取目标对象上该属性的旧值,用于后续的比较和触发更新。
let oldValue = target[key]
if (!this._isShallow) {
const isOldValueReadonly = isReadonly(oldValue)
// value 不是浅响应式,且不是只读的
if (!isShallow(value) && !isReadonly(value)) {
// value 和 oldValue 转换为原始对象
oldValue = toRaw(oldValue)
value = toRaw(value)
}
}
// 判断目标对象上是否存在该属性
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
// set设置新值
const result = Reflect.set(
target,
key,
value,
isRef(target) ? target : receiver,
)
// 触发更新 ===========
if (target === toRaw(receiver)) {
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
} // ***
// 返回设置结果 true false
return result
}触发 trigger 方法
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
): void {
debugger;
const depsMap = targetMap.get(target);
if (!depsMap) {
// never been tracked
globalVersion++;
return;
}
const run = (dep: Dep | undefined) => {
if (dep) {
if (__DEV__) {
dep.trigger({
target,
type,
key,
newValue,
oldValue,
oldTarget,
});
} else {
dep.trigger(); /*****trigger*****/
}
}
};
startBatch();
// schedule runs for SET | ADD | DELETE
if (key !== void 0 || depsMap.has(void 0)) {
run(depsMap.get(key)); // 执行run
}
endBatch(); // 进入任务调度
}