在文中我们分析了 ref 的实现,本节我们来看看另一个核心响应式 API ------ reactive 的实现原理。reactive 主要用于为对象类型创建响应式代理。
实例引入
首先通过一个例子来看看 reactive 的用法:
js
import { reactive } from "vue";
// 创建响应式对象
const state = reactive({
count: 0,
list: [1, 2, 3],
user: {
name: "Tom",
age: 20,
},
});
// 直接访问和修改
console.log(state.count); // 0
state.count++;
// 深层响应式
state.user.age = 21;
state.list.push(4);
这个例子展示了 reactive 的特点:
- 直接访问和修改属性,不需要 .value
- 支持深层响应式转换
- 支持复杂数据结构(数组、嵌套对象等)
核心数据结构
1. WeakMap 缓存结构
js
// 存储原始对象到代理对象的映射
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>();
2. 目标类型枚举
js
enum TargetType {
INVALID = 0, // 无效类型
COMMON = 1, // 普通对象/数组
COLLECTION = 2, // 集合类型(Set/Map等)
}
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;
}
}
3. reactive 工厂函数
js
export function reactive<T extends object>(target: T): Reactive<T> {
// 如果是只读的,直接返回
if (isReadonly(target)) {
return target;
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
);
}
4. 创建响应式对象
js
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
// 非对象直接返回
if (!isObject(target)) {
return target;
}
// 已有代理则返回缓存
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
// 获取目标类型
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;
}
4. Proxy 处理器实现
reactive 使用两种处理器来拦截不同类型的操作:
4.1 基础处理器(baseHandlers)
js
// 用于普通对象和数组的处理器
export const mutableHandlers: ProxyHandler<object> = {
get(target: object, key: string | symbol, receiver: object) {
// 1. 处理特殊 key
if (key === ReactiveFlags.IS_REACTIVE) {
return true;
}
// 2. 获取原始值
const res = Reflect.get(target, key, receiver);
// 3. 依赖收集
track(target, TrackOpTypes.GET, key);
// 4. 处理嵌套对象 - 实现深层响应式
if (isObject(res)) {
return reactive(res);
}
// 5. 解包 ref
if (isRef(res)) {
return res.value;
}
return res;
},
set(target: object, key: string | symbol, value: unknown, receiver: object) {
// 1. 获取旧值
const oldValue = (target as any)[key];
// 2. 设置新值
const result = Reflect.set(target, key, value, receiver);
// 3. 触发更新
if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue);
}
return result;
},
// 拦截 in 操作符
has(target: object, key: string | symbol) {
const result = Reflect.has(target, key);
track(target, TrackOpTypes.HAS, key);
return result;
},
// 拦截 delete 操作
deleteProperty(target: object, key: string | symbol) {
// 1. 检查属性是否存在且可配置
const hadKey = hasOwn(target, key);
const oldValue = (target as any)[key];
// 2. 执行删除
const result = Reflect.deleteProperty(target, key);
// 3. 如果删除成功且属性确实存在,触发更新
if (result && hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue);
}
return result;
},
// 拦截 Object.keys 等操作
ownKeys(target: object) {
track(
target,
TrackOpTypes.ITERATE,
isArray(target) ? "length" : ITERATE_KEY
);
return Reflect.ownKeys(target);
},
};
4.2 集合类型处理器(collectionHandlers)
js
// 用于 Map、Set 等集合类型的处理器
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
get(
target: CollectionTypes,
key: string | symbol,
receiver: CollectionTypes
) {
// 1. 获取集合原型上的方法
const targetProto = getProto(target);
const method = targetProto[key];
// 2. 处理特殊方法
if (key === "size") {
track(target, TrackOpTypes.ITERATE, ITERATE_KEY);
return Reflect.get(target, key, target);
}
// 3. 包装集合方法
return function (...args: unknown[]) {
// 3.1 执行原始方法
const result = method.apply(target, args);
// 3.2 依赖收集
if (key === "get" || key === "has") {
track(target, TrackOpTypes.GET, args[0]);
} else if (key === "add" || key === "set" || key === "delete") {
// 3.3 触发更新
trigger(target, TriggerOpTypes.SET, args[0], args[1]);
}
return result;
};
},
};
这些处理器实现了以下核心功能:
-
依赖追踪
- get/has: 通过 track 收集依赖
- ownKeys: 追踪迭代相关操作
-
触发更新
- set: 属性值变化时触发更新
- deleteProperty: 删除属性时触发更新
- 集合方法: add/set/delete 等操作时触发更新
-
深层响应式
- 访问嵌套对象时递归创建响应式代理
- 确保深层属性也具有响应式
-
特殊处理
- Ref 自动解包
- 响应式标记检查
- 集合类型方法包装
-
性能优化
- 缓存已创建的代理
- 避免不必要的更新触发
调用流程
让我们通过示例代码来分析 reactive 的完整调用流程:
1. 创建阶段
当执行 const state = reactive({ count: 0 })
时:
-
调用 reactive() 函数
-
检查目标对象类型
-
通过 createReactiveObject 创建代理:
- 检查缓存
- 确定目标类型
- 创建对应的 Proxy
- 缓存代理对象
2. 访问阶段
当访问 state.count
时:
-
触发 Proxy 的 get 陷阱
-
执行 baseHandlers 中的 get:
- 解包 ref 值
- 追踪依赖
- 返回属性值
3. 修改阶段
当执行 state.count++
时:
-
触发 Proxy 的 set 陷阱
-
执行 baseHandlers 中的 set:
- 对比新旧值
- 设置新值
- 触发更新
4. 深层响应式
对于嵌套属性(如 state.user.age
):
- 首次访问 user 属性时递归创建代理
- 后续操作都会触发对应层级的响应式处理
- 实现深层响应式转换
与 ref 实现的区别
1. 响应式实现方式
ref 的实现:
- 基于类的 getter/setter
- 只拦截 .value 属性
- 内部通过 dep 存储依赖
reactive 的实现:
- 基于 Proxy 的代理
- 拦截所有属性操作
- 每个属性都有自己的 dep
2. 依赖收集
ref:
js
class RefImpl {
get value() {
this.dep.track();
return this._value;
}
}
reactive:
js
const mutableHandlers = {
get(target, key) {
track(target, TrackOpTypes.GET, key);
return Reflect.get(target, key);
},
};
主要区别:
- ref 只在访问 .value 时收集依赖
- reactive 在访问任何属性时都会收集依赖
- reactive 需要为每个属性维护单独的依赖关系
3. 更新触发
ref:
js
class RefImpl {
set value(newVal) {
if (hasChanged(newVal, this._rawValue)) {
this._value = newVal;
this.dep.trigger();
}
}
}
reactive:
js
const mutableHandlers = {
set(target, key, value) {
const oldValue = target[key];
const result = Reflect.set(target, key, value);
if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue);
}
return result;
},
};
主要区别:
- ref 只需要触发单个 dep 的更新
- reactive 需要根据具体属性触发对应的更新
- reactive 支持更多的操作类型(add/delete/set)
4. 嵌套处理
ref:
- 嵌套对象需要手动使用 reactive 转换
- 主要关注 .value 的响应式
reactive:
- 自动递归转换嵌套对象
- 访问时按需转换(惰性)
- 支持复杂数据结构的响应式转换
5. 性能考虑
ref:
- 结构简单,开销小
- 适合原始值的响应式
- .value 访问会带来一定开销
reactive:
- Proxy 拦截带来一定开销
- 惰性转换减少不必要的代理创建
- WeakMap 缓存优化性能
- 适合大型对象的响应式处理
这些实现上的差异也决定了它们的最佳使用场景:
- ref: 适合单个值的响应式管理
- reactive: 适合复杂对象的响应式处理
总结
通过分析可以看到,reactive 的实现主要包含以下几个关键点:
- 使用 WeakMap 缓存代理对象,避免重复创建
- 区分普通对象和集合类型,使用不同的处理器
- 通过 Proxy 实现属性的拦截和响应式
- 支持深层响应式转换
- 自动解包 ref 类型的属性值
这种基于 Proxy 的设计让 reactive 能够提供更完整的响应式能力,特别适合处理复杂的对象数据。