Vue中Proxy与Object.defineProperty的原理

157 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情

对比 Vue3 和 Vue2 ,有很多变化的地方,其中一个就是响应式的实现, Proxy 替代了 Object.defineProperty。

defineProperty 在前文中有介绍过它的原理以及手写方法: Vue进阶 | 实现一个Vue2响应式,那么是为什么 Vue3 使用了 Proxy ,而不再使用 Object.defineProperty 呢?

1、Object.defineProperty

defineProperty 是:数据劫持 + 发布者-订阅者 模式

通过Object.defineProperty()来劫持各个属性的 settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。

也就是,通过getset这两个属性(属性的 getter 函数、属性的 setter 函数):

  • 当访问该属性时,会调用get()
  • 当属性值被修改时,会调用set()

并且在 Vue2 中,对象这种结构在添加新的属性或是进行删除操作,是无法劫持到的,视图不会自动更新,需要借助一些方法,比如:

  • $set()
    • this.$set(this.obj, key, value)
    • Vue.set(this.obj, key, value)
  • 展开运算符
    • this.obj = {...this.obj}
  • assign
    • this.obj = Object.assign({}, this.obj, { newKey: newValue })
  • this.$forceUpdate()(不推荐)
    • 强制更新视图,执行后会触发updated生命周期,且仅影响本身和插槽内的子组件,而不是所有的子组件

但是这样对于数据层级较深的结构进行监听时,会对性能产生影响

2、Proxy

Proxy 会监听对象的所有操作,也可以直接监听到数组的变化

相较 Object.defineProperty():

  • Proxy 直接劫持到整个对象,返回的是一个新对象,所以可以只操作新的对象,Object.defineProperty 只时遍历对象属性,直接进行修改
  • Proxy 有多达 13 种拦截方法,如 apply、ownKeys、deleteProperty、has (Object.defineProperty 没有这些方法)
  • Object.defineProperty 需要遍历所有的属性,监听所有的属性的变化,占用内存较大

实现形式对比

1、Object.defineProperty:

根据具体的 key 对 get 和 set 进行拦截

image.png

2、Proxy:

proxy 不关心具体的 key,拦截的是修改 data 上的任意 key 和读取 data 上的任意 key

image.png