Vue2 Object.defineProperty VS Vue3 Proxy

作者: tww844475003 分类: 前端开发 发布时间: 2023-03-19 22:53

普通的 Javascript 对象作为 data 选项传给应用或组件实例的时候,Vue 会使用带有 getter 和 setter 的处理程序遍历其所有 property,并将其转换为 Proxy。这是 ES6 仅有的特性,但是我们在 Vue3 版本也使用了 Object.defineProperty 支持 IE 浏览器。虽然两者具有相同的 Surface API,但是 Proxy 版本更精简,同时提升了性能。

Vue3 使用 ES6 的 Proxy 作为观察者机制,取代之前使用的 Object.defineProperty。在 Vue3 中,Proxy 会被优先使用,只有浏览器不支持 Proxy 时才使用 Object.defineProperty。

一、Object.defineProperty 存在的问题

Object.defineProperty 只能劫持对象的属性,而 Proxy 是直接代理对象。由于 Object.defineProperty 只能对属性进行劫持,故需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。

Object.defineProperty 无法监控到数组下标的变化,因此直接通过数组的下标给数组设置值时,不能实时响应。为了解决这个问题,经过 Vue 内部处理后可以使用以下几种方法来监听数组:push() 、pop() 、shift() 、unshift() 、splice() 、sort() 、reverse()。

由于只针对了以上7种方法进行了处理,故其他数组的属性是检测不到的,这样会存在一定的局限性。

Object.defineProperty 对新增属性需要手动进行 Observe。由于 Object.defineProperty 劫持的是对象的属性,故新增属性时,需要重新遍历对象,对其新增属性对象使用 Object.defineProperty 进行支持。也正因如此,使用 Vue 给 data 中的数组或对象新增属性时,需要使用 vm.$set 才能保证新增的属性也是响应式的。

二、Proxy 的优点

什么是 Proxy?Proxy 是 ES6 中新增的一个特性,翻译过来的意思是“代理”,即表示由它来“代理”某些操作。Proxy 让我们能够以简洁易懂的方法控制外部对象的访问,其功能非常类似于设计模式中的代理模式。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象进行访问,都必须先通过这层拦截,因此提供了一种可以对外界的访问进行过滤和改写的机制。

Proxy 有如下的两个优点:

  • 可以劫持整个对象,并返回一个新对象
  • Proxy 支持 13 种拦截操作:

get(target, propKey, receiver):拦截对象属性的读取,比如 proxy.foo 和 proxy[‘foo’]

set(target, propKey, value, receiver):拦截对象属性的设置,比如 proxy.foo == v 或 proxy.foo 和 proxy[‘foo’] == v ,返回一个布尔值

has(target, propKey):拦截 propKey in proxy 的操作,返回一个布尔值

deleteProperty(target, propKey):拦截 delete proxy[propKey] 的操作,返回一个布尔值

ownKeys(target):拦截 Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而 Object.keys() 的返回结果仅包括目标对象自身的楞遍历属性

getOwnPropertyDescriptor(target, propKey):拦截 Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象

defineProperty(target, propKey, propDesc):拦截 Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值

preventExtensions(target):拦截 Object.preventExtensions(proxy),返回一个布尔值

getPrototypeOf(target):拦截 Object.getPrototypeOf(proxy),返回一个对象

isExtensible(target):拦截 Object.isExtensible(proxy),返回一个布尔值

setPrototypeOf(target, proto):拦截 Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。

apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如 proxy(…args)、proxy.call(object, …args)、proxy.apply(…)

construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如 new proxy(…args)

前端开发那点事
微信公众号搜索“前端开发那点事”

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注