前几天面试,面试官第一个问题就把我给问懵了,他问我Vue3为什么要用proxy呢?,我在想为什么呢?
Vue3为什么要用proxy?
在Vue 3中使用Proxy主要有以下原因:
首先,Proxy可以更高效地追踪数据变化。Object.defineProperty有一些局限性,比如它只能劫持对象的已有属性,对于新增属性需要额外处理。而Proxy能直接代理整个对象,不管是已有还是新增属性,都可以被感知到变化。例如,当你给一个被Proxy代理的对象添加新属性时,这个操作能够自然地被捕获到,而不需要像之前那样专门去设置一些额外的配置来使新属性变为响应式。
其次,Proxy提供了更完整的拦截功能。它可以拦截多达13种不同的操作,包括属性的读取、赋值、函数调用等诸多方面。相比之下,Object.defineProperty主要是针对属性的读取和写入进行拦截。这使得在Vue 3中,能够在更多场景下实现精细化的响应式控制。
用Proxy实现响应式的原理是什么?
核心原理是拦截对象的操作。当创建一个Proxy对象时,会传入目标对象和一个处理器对象。这个处理器对象定义了各种用于拦截对象操作的方法,如 get 和 set 。通过这些拦截操作,Proxy能够感知到对象的读写行为,进而实现响应式的数据绑定和更新机制。
用最简单的方式使用 proxy 实现类似vue响应式的功能
(即数据变化Dom跟着变)
vue2中用Object.defineProperty是如何实现响应式的?
Object.defineProperty(obj, prop, descriptor)
其中,obj是要定义属性的目标对象,prop是属性名称,descriptor是属性描述符对象,它可以包含像value(属性的值)、writable(是否可写)、enumerable(是否可枚举)、configurable(是否可配置)以及get(访问器属性的get函数)和set(访问器属性的set函数)等属性。
例如,下面用Object.defineProperty实现响应式demo:
可以看到,如果我想给数据增加一个sex的属性,我需要重复定义下面这端代码,这也就解释了为什么proxy代理整个对象,包括新增属性和删除属性。它能直接监听对象的变化,新增属性会自动被代理。而Object.defineProperty只能对对象的已有属性进行数据劫持。对于新增属性,需要额外使用特定方法才能使其变成响应式的
Object.defineProperty(data, 'sex', {
get() {
return this._sex;
},
set(newValue) {
this._sex = newValue;
sexEl.textContent = newValue;
}
});
Proxy(Vue 3)的优点
拦截操作更全面
比如除了常见的属性读取(get)和设置(set)外,还能够拦截deleteProperty(删除属性操作)、has(判断属性是否存在操作)、ownKeys(获取对象自身所有属性名操作)等多种操作
对嵌套对象处理更简洁直观
Proxy 是直接代理整个对象,对于嵌套对象,在访问其嵌套属性时同样会自动触发相应的拦截器,有没有想到vue的ref 和 reactive定义响应式变量的区别?下篇文章会详细介绍
性能优化潜力大
由于 Proxy 的拦截机制相对更加底层和原生,在某些场景下,尤其是大数据量或者频繁操作对象属性的复杂应用场景中,通过合理的优化(比如对不需要响应式的属性进行精准排除等),可以更好地控制性能损耗,挖掘出比 Object.defineProperty 更好的性能表现。
Proxy(Vue 3)的缺点
兼容性问题
Proxy 是 ES6 中引入的新特性,较老版本的浏览器(如 IE 浏览器等)对其支持较差,这意味着如果项目需要兼容这些老旧浏览器,使用 Proxy 实现响应式就需要考虑引入额外的兼容垫片或者进行降级处理等,增加了项目的复杂性和开发成本。
学习成本相对较高
学习成本高?是理由吗?
Object.defineProperty 的优点
兼容性好
Object.defineProperty 在大多数主流浏览器(包括一些较老版本浏览器)中都有较好的支持,不需要过多担心浏览器兼容性问题
相对Proxy来说简单易懂
--
Object.defineProperty 的缺点
拦截操作有限
它只能拦截属性的读取(get)和设置(set)操作,无法对像属性删除、判断属性是否存在等其他常见操作进行拦截和响应式处理
处理嵌套对象繁琐
对于嵌套对象,必须要手动进行递归遍历,逐个属性去使用 Object.defineProperty 进行定义,以确保每个属性都能成为响应式属性
数组响应式处理复杂
对数组进行响应式处理时,需要重写数组的多个变异方法(如push、pop、shift、unshift、splice等)来手动触发依赖更新,实现起来较为复杂且不够优雅,相较于 Proxy 可以自然地对数组变异方法进行拦截并处理响应式的方式,效率和代码简洁性都欠佳。
以下是2024-11-17日更新
另一个能反应vue2的Object.defineProperty 实现响应式的方式是 this.$set(target, propertyName, value)
当调用 this.$set(this.someObject, 'newProperty', value) 时,$set 会使用 Object.defineProperty 将 newProperty 添加到 someObject 中,并将其设置为响应式的。
比如管理后台项目,查询条件模块被封装了一个组件<SearchFrom :data="formData" />
data 部分定义时只写了formData:{}, 实际的查询条件有 formData.title, formData.ptype,formData.deviceType, formData.pageNo。
当我们想增加一个查询条件时formData.sex时,实际就是对响应式变量增加新的属性,这个时候就要用this.$set(formData, 'sex', 0)