如何通过 Vue3 的 Proxy 拦截实现对象 getset 方法,构建响应式数据监听机制?
- 内容介绍
- 文章标签
- 相关推荐
本文共计772个文字,预计阅读时间需要4分钟。
Vue3 的响应式核心依赖于 `Proxy` 来拦截对象的读写操作,在 `get` 时收集依赖,在 `set` 时触发更新,整个过程无需递归初始化,自然支持数组和动态属性。
Proxy 如何拦截 get/set
Proxy 接收目标对象和一个处理器(handler)对象,其中 get 和 set 是两个关键拦截器:
-
get(target, key, receiver):当访问
obj.xxx时触发。这里可以做两件事:一是用Reflect.get()正常取值,二是调用依赖收集函数(如track(target, key)),把当前正在执行的副作用函数(比如渲染函数或 computed)和这个 key 关联起来 -
set(target, key, value, receiver):当赋值
obj.xxx = val时触发。先用Reflect.set()完成真实赋值,再调用触发函数(如trigger(target, key)),通知所有依赖该 key 的副作用函数重新执行
为什么必须搭配 Reflect 使用
直接写 target[key] 或 target[key] = value 会丢失代理语义,尤其在有 getter/setter 或原型链访问时容易出错。而 Reflect.get 和 Reflect.set 是规范化的操作封装,能正确处理:
– 原型链上的属性访问
– 访问器属性(getter/setter)
– receiver 参数控制 this 指向(确保在 proxy 内部调用方法时,this 仍指向 proxy 实例)
Vue3 所有拦截器内部都统一使用 Reflect 方法,保证行为可预测、可复现。
如何支持嵌套对象和数组
Proxy 默认只代理第一层,深层数据需要“懒代理”策略:
立即学习“前端免费学习笔记(深入)”;
- 在
get中,若返回值是对象或数组,就递归调用reactive()包装它,下次访问其属性时才会创建下一层 Proxy - 数组不用特殊处理 push/pop —— 因为 Proxy 能直接拦截数组实例的方法调用(如
arr.push()),但 Vue3 还额外劫持了Map/Set的方法(如get、add),通过函数重写实现依赖追踪,避免把arr.get误当成 key 来收集
和 Vue2 的 defineProperty 对比优势
Object.defineProperty 必须提前遍历所有属性,无法监听新增属性或数组索引变化;而 Proxy:
- 一次性代理整个对象,不关心初始有哪些 key
- 天然捕获
in、for...in、delete等操作(分别由has、ownKeys、deleteProperty拦截) - 数组长度变化、索引赋值、push/unshift 等全部自动响应,无需重写原型方法
本文共计772个文字,预计阅读时间需要4分钟。
Vue3 的响应式核心依赖于 `Proxy` 来拦截对象的读写操作,在 `get` 时收集依赖,在 `set` 时触发更新,整个过程无需递归初始化,自然支持数组和动态属性。
Proxy 如何拦截 get/set
Proxy 接收目标对象和一个处理器(handler)对象,其中 get 和 set 是两个关键拦截器:
-
get(target, key, receiver):当访问
obj.xxx时触发。这里可以做两件事:一是用Reflect.get()正常取值,二是调用依赖收集函数(如track(target, key)),把当前正在执行的副作用函数(比如渲染函数或 computed)和这个 key 关联起来 -
set(target, key, value, receiver):当赋值
obj.xxx = val时触发。先用Reflect.set()完成真实赋值,再调用触发函数(如trigger(target, key)),通知所有依赖该 key 的副作用函数重新执行
为什么必须搭配 Reflect 使用
直接写 target[key] 或 target[key] = value 会丢失代理语义,尤其在有 getter/setter 或原型链访问时容易出错。而 Reflect.get 和 Reflect.set 是规范化的操作封装,能正确处理:
– 原型链上的属性访问
– 访问器属性(getter/setter)
– receiver 参数控制 this 指向(确保在 proxy 内部调用方法时,this 仍指向 proxy 实例)
Vue3 所有拦截器内部都统一使用 Reflect 方法,保证行为可预测、可复现。
如何支持嵌套对象和数组
Proxy 默认只代理第一层,深层数据需要“懒代理”策略:
立即学习“前端免费学习笔记(深入)”;
- 在
get中,若返回值是对象或数组,就递归调用reactive()包装它,下次访问其属性时才会创建下一层 Proxy - 数组不用特殊处理 push/pop —— 因为 Proxy 能直接拦截数组实例的方法调用(如
arr.push()),但 Vue3 还额外劫持了Map/Set的方法(如get、add),通过函数重写实现依赖追踪,避免把arr.get误当成 key 来收集
和 Vue2 的 defineProperty 对比优势
Object.defineProperty 必须提前遍历所有属性,无法监听新增属性或数组索引变化;而 Proxy:
- 一次性代理整个对象,不关心初始有哪些 key
- 天然捕获
in、for...in、delete等操作(分别由has、ownKeys、deleteProperty拦截) - 数组长度变化、索引赋值、push/unshift 等全部自动响应,无需重写原型方法

