用 Proxy 实现深度嵌套监听的轻量级响应式引擎,核心是 get 中按需递归代理对象、set 中对比新旧值触发更新通知,并用 WeakMap 缓存代理避免重复和内存泄漏。

用 Proxy 实现深度嵌套监听的轻量级响应式引擎,核心在于:用 get 拦截触发递归代理,用 set 拦截触发更新通知,同时避免重复代理、绕过逻辑和无限循环。它不依赖框架,几十余行就能跑通关键路径。
一、基础代理 + 深层递归
Proxy 默认只代理第一层,要监听 obj.user.profile.name 这类嵌套路径,必须在 get 中判断返回值是否为对象,并自动对其创建新 Proxy。
- 每次访问属性时,若值是普通对象(非 null、非原始类型),就调用自身
reactive()生成代理,再返回 - 用
Reflect.get保证读取行为语义正确,包括原型链、this绑定等 - 不提前遍历所有属性,真正“按需代理”,节省初始化开销
二、更新通知与变化检测
set 拦截不是简单赋值,而是要识别真实变更、防止误触发,并把更新信号发出去。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
- 先用
Reflect.get取出旧值,对比oldValue !== value再决定是否通知 - 通知函数(如
callback(key, value))应接收足够上下文:目标对象、键名、新旧值 - 数组索引赋值(
arr[0] = x)、length修改、push/pop等操作都会命中set,天然支持
三、防坑要点:缓存、引用、执行控制
轻量 ≠ 简陋。几个关键细节决定是否稳定可用:
立即学习“前端免费学习笔记(深入)”;
- 用
WeakMap缓存已代理的对象,避免对同一对象反复 new Proxy,也防止内存泄漏 - 不要直接替换闭包内维护的依赖数组(如
effects = []),改用effects.length = 0或effects.splice(0) - 若在 effect 中读写同一属性(如计算属性自依赖),需加执行标记(如
isRunning = true)阻断循环
四、极简可运行示例
以下代码可直接粘贴运行,支持多层嵌套、数组操作、动态新增属性:
function reactive(obj, callback) {
const cache = new WeakMap();
function createProxy(target) {
if (target == null || typeof target !== 'object') return target;
if (cache.has(target)) return cache.get(target);
const proxy = new Proxy(target, {
get(obj, key, receiver) {
const res = Reflect.get(obj, key, receiver);
if (typeof res === 'object' && res !== null) {
return createProxy(res);
}
return res;
},
set(obj, key, value, receiver) {
const oldValue = obj[key];
const result = Reflect.set(obj, key, value, receiver);
if (oldValue !== value) callback({ key, oldValue, newValue: value, target: obj });
return result;
}
});
cache.set(target, proxy);
return proxy;
}
return createProxy(obj);
}
// 使用
const state = reactive(
{ user: { name: 'Alice', tags: ['js'] } },
({ key, newValue }) => console.log(`→ ${key} changed to`, newValue)
);
state.user.name = 'Bob'; // → name changed to Bob
state.user.tags.push('ts'); // → 0 changed to ts(因 push 触发索引赋值)

















