Vue2 的响应式:Object.defineProperty
Vue2是遍历对象的每一个属性,给单个属性单独设置getter/setter来监听。
Vue3对比Vue2的优势(通俗+面试要点)
- ✅ 能监听新增、删除属性
Vue2:初始化的时候遍历属性。后续新加的属性、删掉属性,监听不到 ,需要用$set。
Vue3:Proxy代理整个对象 ,不是单个属性,新增/删除属性都能拦截,不用$set。
- ✅ 原生数组可以完整监听
Vue2重写了数组7个方法,但是通过索引修改数组、修改数组length,监听不到。
Vue3 Proxy直接代理数组,数组下标赋值、修改length都能捕获。
- ✅ 可以监听 Map、Set 这类集合类型
Vue2只能监听普通对象、数组;
Vue3 Proxy天然支持 Map / Set / WeakMap / WeakSet。
- ✅ 懒代理(性能更好)
Vue2初始化,递归遍历全部嵌套对象,一次性把所有属性加上get/set,对象很大的时候初始化慢。
Vue3:外层对象先代理,嵌套对象不会立刻处理,访问到内层属性的时候才去代理。节省初始化开销,大数据场景更快。
- ✅ 代码层面:
Object.defineProperty只能监听属性读写;Proxy能拦截更多操作(in判断、delete等)
短板顺带提(面试常问)
Proxy不支持IE浏览器,Vue3直接放弃IE;Vue2可以兼容IE。
🎤面试口述精简版(直接背)
Vue3响应式核心是Proxy + Reflect。Proxy代理整个目标对象,拦截读写、新增、删除等操作,配合track收集依赖、trigger触发更新。
Vue2使用Object.defineProperty,只能预先遍历属性监听。相比Vue2:
可以监听新增、删除属性,不再需要$set;
支持数组下标、length修改监听;
支持Map、Set等集合;
懒代理,初始化性能更好;
缺点是Proxy无法兼容IE浏览器。
- 要不要我出一道Vue2/Vue3响应式的面试问答题,你来口述回答?
Proxy 详解(面试核心)
基础信息
读音:英 /ˈprɒksi/ ,美 /ˈprɑːksi/ ,中文近似谐音:普若克西
单词本意:代理。ES6 的 Proxy 就是代理对象,用来拦截目标对象的操作。
const p = new Proxy(target, handler) // target:要代理的目标对象 // handler:拦截器,自定义捕获函数(get/set 等)
易混区分与注意点
-
Reflect(/rɪˈflekt/,反射):常和 Proxy 搭配使用
-
Proxy 不能直接监听普通原始基本类型,只能代理对象、数组这类引用类型
一句话面试简述
Proxy 用于创建一个对象的代理,从而实现基本操作的拦截和自定义(属性读取、赋值、删除等),可以监听对象,是 Vue3 响应式的核心原理。
Vue2 vs Vue3 响应式对比
面试最爱问:为什么 Vue3 要用 Proxy 重写响应式?对比表直接背:
| 对比维度 | Vue2:Object.defineProperty | Vue3:Proxy |
|---|---|---|
| 核心机制 | 劫持对象已有属性的 getter / setter | 代理整个对象,拦截对对象的操作 |
| 新增 / 删除属性 | ❌ 监听不到,需 Vue.set / Vue.delete 补救 | ✅ 天然支持(has / deleteProperty 拦截) |
| 数组监听 | ⚠️ 下标赋值、length 变化监听不到;重写 push/pop 等 7 个方法部分实现 | ✅ 天然支持索引赋值、length 变化 |
| 嵌套对象 | 初始化时递归遍历、一次性深度劫持(开销大) | ✅ 懒代理:访问到嵌套对象时才递归代理 |
| 拦截能力 | 仅 get / set(数组靠方法重写) | 13 种捕获器:get / set / has / deleteProperty / ownKeys / apply / construct 等 |
| 支持的容器 | 普通对象、数组 | 额外支持 Map、Set、WeakMap、WeakSet |
| 性能 | 初始化递归开销大,深层对象卡顿 | 懒代理 + 按需触发,整体更优 |
| 兼容性 | IE9+(IE8 不支持) | IE 全系不支持(Vue3 放弃 IE11) |
| 语言层面 | JS 的补丁式方案 | ES6 原生特性 |
Proxy 面试常见问题(逐题)
Q1:Proxy 和 Object.defineProperty 有什么区别?
核心区别:defineProperty 劫持的是单个已存在属性的读写 ,需要遍历对象逐个定义,新增/删除属性天然管不到;Proxy 代理的是整个对象,任何对对象的操作(读取、赋值、删除、遍历、函数调用等)都会被拦截,覆盖面更全。
Q2:为什么 Vue3 用 Proxy 替代 Object.defineProperty?
解决 Vue2 的三大痛点:① 新增 / 删除属性监听不到;② 数组下标赋值和 length 变化监听不到;③ 初始化要递归遍历所有属性,深层对象性能开销大。Proxy 配合懒代理,这三点全部解决,还顺带支持 Map / Set。
Q3:为什么 Proxy 不能直接监听基本类型?
因为基本类型(string / number / boolean 等)没有可拦截的属性操作,new Proxy(1, {}) 会直接报错(target 必须是非基本类型对象)。所以 Vue3 用 ref 把基本类型包装成 { value } 对象后再代理。
Q4:Proxy 有哪些捕获器(trap)?
共 13 种:get、set、has、deleteProperty、ownKeys、getOwnPropertyDescriptor、defineProperty、getPrototypeOf、setPrototypeOf、isExtensible、preventExtensions、apply、construct。Vue3 核心只用 get(收集依赖)和 set(触发更新),再配合 has / deleteProperty / ownKeys 处理新增、删除、遍历。
Q5:Reflect 有什么作用?为什么和 Proxy 搭配使用?
Reflect 提供与 Proxy 捕获器一一对应 的默认方法(Reflect.get / Reflect.set / Reflect.has...)。搭配原因:① this 指向正确------Reflect.get(target, key, receiver) 可显式指定 receiver,避免 getter 里 this 指向错误;② set 返回布尔值,能判断赋值是否成功;③ 与捕获器同名同参,语义统一、代码简洁。
Q6:Vue3 的 reactive 是怎么实现的?
核心就是 new Proxy(target, handler):在 get 中调用 track 收集依赖(effect),在 set 中调用 trigger 触发更新。嵌套对象采用懒代理:get 时发现属性值还是对象,才递归 reactive,访问到才代理,性能更好。
Q7:Proxy 有什么局限性?
① 只能代理引用类型,基本类型要靠 ref 包装;② 兼容性:IE 全系不支持,Vue3 因此放弃 IE11;③ 相比直接属性访问有性能损耗;④ 代理后对象身份变化(=== 判断失效),个别依赖身份判断的库可能受影响。
Q8:Vue2 为什么监听不到数组下标变化?
defineProperty 只能劫持已存在的属性,数组下标赋值(arr0 = x)和 length 变化不会触发 setter。Vue2 靠重写 push / pop / shift / unshift / splice / sort / reverse 这 7 个方法实现部分监听,但 arr0 = x 这类操作依然监听不到,只能靠 Vue.set 补救。