一、定义
Proxy(ES6) :原生 API,语法 new Proxy(target, handler)。给整个目标对象包一层拦截壳,通过 handler 捕获器拦截读、改、增、删等操作。
- target:被监控的原始对象(对象、数组、Map、Set)
- handler:捕获器钩子,常用:
get、set、deleteProperty - Reflect:配套 API,方法和捕获器一一对应,规范对象操作、处理 this、返回布尔结果。
Object.defineProperty(ES5) :只能给对象单个已存在属性设置 getter/setter,仅拦截该属性的读取和赋值。
二、Vue3 Proxy 响应式原理(reactive)
reactive 内部 new Proxy 包装对象。
- 模板读取属性 → 触发
get→ 依赖收集(记录当前渲染函数) - 修改 / 新增 / 删除属性 → 触发
set/deleteProperty→ 派发更新(执行收集的渲染函数,刷新页面) - 懒代理:嵌套对象不会一开始递归全部代理;只有访问到内层对象时,才给它创建 Proxy,节省初始化性能。
👉 数组特殊说明 数组本质也是对象,下标就是 key,Proxy 原生就能拦截:
const arr = new Proxy([1,2,3], {
set(){ console.log('数组变了') }
})
arr[0] = 999 // ✅ 可以捕获!Vue2 不行
arr.length = 0 // ✅ 也可以捕获
Vue2 只能 hack 重写 push/pop/splice 等 7 个数组方法,通过下标、修改 length 无法监听,Proxy 原生支持。
三、Proxy 局限
- 不兼容 IE,Vue3 放弃 IE 支持
- 只能代理引用类型(对象、数组、Map、Set);基础类型不能直接代理,所以基础类型响应式用
ref,内部包装成{value: xxx} - 只有操作代理对象才会触发拦截,直接修改原始 target 对象,监控无效
四、对比
表格
| 对比项 | Vue2 defineProperty | Vue3 Proxy |
|---|---|---|
| 监听粒度 | 单个已有属性 | 整个对象 |
| 新增 / 删除属性 | ❌ 监听不到,需要 $set | ✅ 原生支持 |
| 数组 | 重写 7 个方法,下标、length 不响应 | ✅ 下标、length 都监听 |
| 深层对象 | 初始化递归全部劫持,开销大 | 懒代理,访问到才劫持 |
| Map/Set | ❌ 不支持 | ✅ 支持 |
| 浏览器 | 兼容 IE | ❌ IE 不支持 |
✅ 核心
Proxy 是 ES6 代理 API,给整个对象加拦截壳,搭配 Reflect;Vue3 reactive 通过 get 收集依赖、set/deleteProperty 触发更新,懒代理优化性能,原生支持数组下标、length 修改。Vue2 的 defineProperty 只能劫持单个旧属性,无法监听新增删除属性和数组下标,Proxy 弥补了这些问题。
一句话总结
Proxy 是给整个对象套一层拦截器,可以捕获对象读取、修改、新增、删除等操作;Vue3 的 reactive 借助 Proxy 拦截操作,在 get 收集依赖,set 触发更新,搭配 Reflect 保证操作规范;并且是懒代理,嵌套对象访问的时候才做响应式