ES6 Class 本身不带响应式能力,因存取器需提前声明属性且无法监听动态新增、数组操作及嵌套对象变化;Proxy 可递归拦截所有读写并配合 WeakMap 缓存实现真正可观测状态树。

ES6 Class 本身不带响应式能力,存取器(get/set)只能监听已声明的属性,无法自动覆盖新增属性或嵌套对象;真正实现“可观测状态树”的关键,在于用 Proxy 替代传统 getter/setter,再由 Class 封装初始化逻辑与依赖管理机制。
为什么不能只靠 class 存取器?
Class 中的 get/set 是静态定义的:必须提前写出每个属性名,且对数组索引、动态 key、深层嵌套对象完全失效。例如:
-
state.user.name可监听,但state.user.profile.avatar若profile是后续赋值的对象,其内部变化不会触发更新 -
state.list[0] = 'a'或state.list.push('b')不会进入任何 set 拦截 - 新增属性如
state.newField = 123在非 Proxy 场景下根本不可观测
用 Proxy 构建可递归代理的状态根
在 Class 构造函数中,不直接暴露原始数据,而是返回一个 Proxy 实例,把所有读写操作统一拦截:
-
get拦截:检测返回值是否为对象,若是则递归调用reactive()创建新代理(懒代理,避免无限递归) -
set拦截:赋值后触发对应字段的所有副作用函数(如渲染函数、计算属性),并支持数组 length 变更、索引赋值等场景 - 使用
WeakMap缓存已代理对象,避免重复代理同一对象
Class 封装依赖收集与触发逻辑
响应式核心不是“监听”,而是“谁在读、谁该更新”。Class 可提供:
安全更新和维护 CLI Proxy API(CPA)部署与配置。用于 CPA 镜像升级、配置变更、认证目录兼容修复、上线验证与回滚。适用于用户提到“CPA 更新/升级/配置改了/容器重建/回滚”等场景。
-
effect(fn)方法:执行 fn 前激活一个全局“当前副作用”,并在get拦截中将该副作用注册到属性对应的依赖集合里 -
trigger(key)方法:在set后根据 key 找出所有订阅者并执行 - 支持嵌套路径依赖:例如
effect(() => state.a.b.c)应同时收集a、b、c三级依赖
完整可运行结构示意
一个极简但可扩展的实现骨架如下:
class ReactiveState {
constructor(initial) {
// 用 WeakMap 避免内存泄漏
this.depMap = new WeakMap();
return this.createProxy(initial);
}
<p>createProxy(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
if (this.depMap.has(obj)) return this.depMap.get(obj);</p><pre class="brush:php;toolbar:false;">const handler = {
get: (target, key, receiver) => {
const res = Reflect.get(target, key, receiver);
// 收集依赖(需配合 activeEffect 使用)
this.track(target, key);
// 懒递归代理
return typeof res === 'object' && res !== null
? this.createProxy(res)
: res;
},
set: (target, key, value, receiver) => {
const result = Reflect.set(target, key, value, receiver);
// 触发更新
this.trigger(target, key);
return result;
}
};
const proxy = new Proxy(obj, handler);
this.depMap.set(obj, proxy);
return proxy;}
track(target, key) { / 依赖收集 / } trigger(target, key) { / 触发更新 / } }
这样构造出的实例,既是 Class 实例,又是 Proxy 对象,天然支持任意深度、任意动态增删改查的可观测性。










