vue3.0为何要用Proxy替代defineProperty

写在前面

若是你对Vue发布订阅的响应式实现有必定的了解,那么你必定知道defineProperty。react

Object.defineProperty(obj, prop, descriptor)es6

Vue2.x中的响应式实现正是基于defineProperty中的descriptor,经过属性的getter、setter监听来实现的。typescript

同时你应该知道的是,咱们在Vue中使用下标的方式直接修改属性的值或者添加一个预先不存在的对象属性是没法作到setter监听的,这是defineProperty的局限性。数组

而咱们在Vue中使用的push、pop、shift、unshift、splice等一系列修改原数组的方法,其实不是原生的数组方法,都是被Vue修改过的,其中加入了响应代码。性能

有消息称Vue3.0的响应式将会使用Proxy来实现,这一举动无疑优化了Vue的响应能力,可是也决定了Vue将会完全抛弃IE阵营,咱们是否应该默(qing)哀(zhu) 一分钟。测试

defineProperty中的getter、setter

defineProperty的局限性的最大缘由是它只能针对单例属性作监听,Vue2.x中对data中的属性作了遍历 + 递归,为每一个属性设置了getter、setter。优化

这也就是为何Vue只能对data中预约义过的属性作出响应的缘由。ui

defineProperty的用法

var val = 1
    var obj = Object.defineProperty({}, 'sum', {
        enumerable: true,
        configurable: true,
        get() {
            return val
        },
        set(newValue) {
            val += newValue
        }
    })
复制代码

测试代码:

测试代码

咱们也能够这样写:spa

var val = 1
    var obj = {
        get ['sum']() {
            return val
        },
        set ['sum'](newValue) {
            val += newValue
        }
    }
复制代码

这段代码和上面代码结果是同样的。3d

这里的'sum'就是被监听的属性名,也就是咱们须要监听的“一个”属性。

正是由于使用defineProperty每次只能绑定一个属性监听,因此Vue在遍历 + 递归时要有更大的性能消耗和更多的代码。

Proxy中的getter、setter

Vue3.0终于要使用Proxy,就像上文说的同样,首先应该对IE表示沉(xi)痛(wen)哀(le)悼(jian)。

Proxy 能够理解成,在目标对象以前架设一层“拦截”,外界对该对象的访问,都必须先经过这层拦截,所以提供了一种机制,能够对外界的访问进行过滤和改写。

也就是说,Proxy的监听是针对一个对象的,那么对这个对象的全部操做会进入监听操做,这就彻底能够代理全部属性了。

更多Proxy用法能够看 阮一峰大神的ES6入门-Proxy,本文只讲解getter、setter实现。

使用Proxy监听

var data = {
        sum: 0
    }
    var proxy = new Proxy(data, {
        get(target, property) {
            return target[property]
        },
        set(target, property, value) {
            target[property] += value
        }
    })
复制代码

Proxy测试

Proxy对象测试

当data为数组时:

Proxy数组测试

Proxy中getter、setter说明

get接收三个参数: get(target, propKey, receiver)

target是被监听对象,在示例代码中表示的是data propKey是属性名,即当前须要获取的属性名 receiver是监听对象,即Proxy实例,在示例代码中表示的是proxy

set接收四个参数: get(target, propKey, value, receiver)

targetpropKeyreceiver三个参数和get中同样 value表示被修改的值,即测试代码中的1

很明显的是,咱们使用Proxy时并不须要关心某一个具体的属性,而且Proxy实例上的修改不会影响到被监听对象target中的值(前提是你不主动修改target)。

Proxy中getter、setter的局限性

让咱们看看如下代码:

var data = {
        a: { b: 0 }
    }
    var proxy = new Proxy(data, {
        get(target, property) {
            console.log(`path: ${property}`)
            return target[property]
        },
        set(target, property, value) {
            console.log(`path: ${property}`)
            target[property] += value
        }
    })
复制代码

打印结果:

打印结果

很明显的是,proxy代理getter、setter只会关注浅层数据,而不会返回深层路径。

若是path在这里可以返回a.b那么将会带来一个很好的收益,不过既然Proxy规范没有实现那么咱们也是机关用尽。

并且从理解上来讲修改proxy.a.b返回的修改对象名是a貌似也是正确的,如今咱们须要作的是记住这一结论就好。

结语

Proxy和defineProperty中getter、setter的用法就说到这里。

能够预见的是,Vue3.0中使用Proxy代理将会带来很大的性能提高和更优的代码。

可是就像react中约定的同样,对于引用类型值的响应式的支持可能会变得比之前更不友好(须要开发者实现浅拷贝赋值)。

不过若是尤大依旧选择使用递归的方式来解决对象类型数据的响应式,我我的看来是有些得不偿失的。

从我本身的意愿来看,我更但愿是借助于immutable,而不是走递归遍历的老路。

相信尤大必定会有本身的考量,最终的结果确定是令大部分人满意的方案。

同时Vue3支持typescript的方向更是令我激动不已,做为尤大的粉丝,咱们好好期待就是了。

相关文章
相关标签/搜索