响应式系统中的“单例模式”指对同一源对象多次调用 reactive() 仅创建一个 Proxy 并复用,通过 WeakMap 缓存源对象到代理的唯一映射,避免重复代理导致的更新冗余、依赖收集错乱和引用不一致问题。

响应式系统里的“单例模式”,不是指传统意义上的类只有一个实例,而是指:对同一个原始对象(源对象),无论调用多少次 reactive(),都只创建一个 Proxy 代理,并复用它。避免重复代理造成内存浪费、依赖收集错乱或响应式失效。
为什么需要单例?
如果不做控制,每次 reactive(obj) 都新建一个 Proxy,会出现:
- 同一份数据被多个代理监听,set 操作触发多次重复更新
- 依赖收集时,不同代理往不同 bucket 中存副作用函数,修改 obj.age 可能漏掉某些 effect
- obj === reactive(obj) 返回 false,破坏引用一致性,影响 shallowRef、toRaw 等逻辑
核心实现:WeakMap 缓存源对象 → 代理对象映射
Vue 3 的 reactive 内部正是用 WeakMap 建立源对象到代理的唯一映射。关键代码逻辑如下(精简自源码):
// reactive.js
const proxyMap = new WeakMap() // key: 原始对象,value: 对应的 Proxy
function reactive(target) {
if (!isObject(target)) return target
// 已代理过?直接返回缓存的代理
const existingProxy = proxyMap.get(target)
if (existingProxy) return existingProxy
// 否则新建 Proxy,并缓存
const proxy = new Proxy(target, handler)
proxyMap.set(target, proxy)
return proxy
}
实际效果示例
运行以下代码:
const obj = { count: 0 }
const p1 = reactive(obj)
const p2 = reactive(obj)
console.log(p1 === p2) // true ← 单例生效
console.log(p1 === obj) // false(正常)
console.log(proxyMap.size) // 1 ← 只存了一份映射
注意:WeakMap 是关键
使用 WeakMap 而非普通 Map,是因为:
- 键必须是对象,天然适配“源对象”作为 key
- 不阻止垃圾回收——当源对象被销毁,WeakMap 中对应条目自动释放,不会内存泄漏
- 无法遍历,保证内部映射不被外部篡改

















