1. 基本概念
Object.defineProperty
- ES5 引入的方法,用于在对象上定义新属性或修改现有属性
- 可以精确控制属性的特性(可枚举、可配置、可写等)
Proxy
- ES6 引入的特性,用于创建一个对象的代理
- 可以拦截和自定义对象的各种操作(属性查找、赋值、枚举等)
2. 在 Vue 中的应用关系
Vue 2 使用 defineProperty
// Vue 2 响应式原理简化版
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`获取 ${key}: ${val}`)
// 依赖收集
return val
},
set(newVal) {
console.log(`设置 ${key}: ${newVal}`)
if (newVal !== val) {
val = newVal
// 触发更新
}
}
})
}
const data = {}
defineReactive(data, 'name', 'Vue')Vue 3 使用 Proxy
// Vue 3 响应式原理简化版
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
console.log(`获取 ${key}`)
// 依赖收集
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log(`设置 ${key}: ${value}`)
const result = Reflect.set(target, key, value, receiver)
// 触发更新
return result
}
})
}
const data = reactive({ name: 'Vue' })3. 主要差异对比
| 特性 | defineProperty | Proxy |
|---|---|---|
| 兼容性 | IE8+ | IE不支持,现代浏览器 |
| 监听范围 | 单个属性 | 整个对象 |
| 新增属性 | 无法监听 | 可以监听 |
| 删除属性 | 无法监听 | 可以监听 |
| 数组变化 | 需要特殊处理 | 原生支持 |
| 性能 | 需要递归遍历 | 惰性代理 |
4. 具体差异详解
新增/删除属性
// defineProperty 的局限
const obj = {}
Object.defineProperty(obj, 'name', {
get() { return this._name },
set(val) { this._name = val }
})
obj.name = 'test' // 可以监听
obj.age = 25 // 无法监听到新增属性
// Proxy 的优势
const proxyObj = new Proxy({}, {
set(target, key, value) {
console.log(`设置 ${key}: ${value}`)
target[key] = value
return true
}
})
proxyObj.name = 'test' // 可以监听
proxyObj.age = 25 // 也可以监听到数组处理
// defineProperty 对数组的处理
// Vue 2 需要重写数组方法
const arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
arrayMethods.forEach(method => {
Array.prototype[method] = function(...args) {
console.log(`数组方法 ${method} 被调用`)
// 触发更新
return originalMethod.apply(this, args)
}
})
// Proxy 可以直接监听数组
const arr = new Proxy([], {
set(target, key, value) {
console.log(`数组索引 ${key} 被设置为 ${value}`)
target[key] = value
return true
}
})5. 性能对比
defineProperty
- 需要在初始化时递归遍历所有属性
- 每个属性都需要单独定义 getter/setter
- 对深层嵌套对象性能影响较大
Proxy
- 惰性代理,只有访问时才会触发
- 可以代理整个对象,无需遍历属性
- 对深层对象有更好的性能表现
6. 实际应用场景
defineProperty 适用场景
- 需要兼容老版本浏览器
- 只需要监听特定属性
- 对性能要求不高的简单场景
Proxy 适用场景
- 现代浏览器环境
- 需要监听对象的所有变化
- 复杂的响应式系统(如 Vue 3)
- 需要拦截多种操作类型
7. 代码示例对比
// defineProperty 实现简单响应式
function observeWithDefineProperty(obj) {
Object.keys(obj).forEach(key => {
let val = obj[key]
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}`)
return val
},
set(newVal) {
console.log(`修改 ${key} 为 ${newVal}`)
val = newVal
}
})
})
}
// Proxy 实现响应式
function observeWithProxy(obj) {
return new Proxy(obj, {
get(target, key) {
console.log(`读取 ${key}`)
return target[key]
},
set(target, key, value) {
console.log(`修改 ${key} 为 ${value}`)
target[key] = value
return true
},
deleteProperty(target, key) {
console.log(`删除 ${key}`)
delete target[key]
return true
}
})
}