VUE3.X为何弃用Object.defineProperty?

1,123 阅读4分钟

本人正在前端历劫中,写文章的主要目的是为了让自己理解知识更透彻,忘了随时可以翻阅一下。如果同时也能帮到其他正在历劫的小伙伴当然更高兴l。

前面一篇文章写了关于VUE2.0中实现数据双向绑定的核心知识点--Object.defineProperty,今天继续探讨下VUE2.X为何弃用Object.defineProperty。

一门技术的被弃用,肯定是因为技术本身存在诸多限制或不足或有了更完美的替代品。

首先探讨下VUE2.0中Object.defineProperty存在的问题\

为了让读者有更直观的一个印象,我这里先将VUE2.0中Object.defineProperty存在的问题罗列出来

  1. 不能监听数组索引和长度的变更
  2. 无法监听 属性的添加和删除
  3. 必须遍历对象的每个属性, 必须深层遍历嵌套的对象。Object.defineProperty是对对象属性的操作,所以需要对对象进行深度遍历去对属性进行操作。

注:很多小伙伴可能有疑问,不是说VUE2.0中Object.defineProperty不能监听数组索引和长度的变更吗,为什么我们在使用vue2的时候,一样可以监听数组索引和长度的变更? 我们得明白主体,我们只是说Object.defineProperty不能,并没说vue2.0不能。并不能说因为vue2.0使用了Object.defineProperty,vue2.0就不能监听数组内部属性的变化了。而vue2之所以能监听,是vue2.0对数组相关的方法或其他进行了重写。当然vue2.0中还是存在无法监听直接修改数组中某一项值和数组长度,如ar[0]=1, arr.length=12是无法监听的,针对这个vue2.0有其他解决方案,请看下文。

问题1、不能监听数组索引和长度的变更

例:

        const obj = {};
        let initValue = 1;

        Object.defineProperty(obj, 'name', {
            set: function(value) {
                console.log('set方法被执行了');
                initValue = value;
            },
            get: function() {
                return initValue;
            }
        });
        console.log(obj.name); // 1
        
        obj.name = []; // 会执行set方法,会打印信息

        // 给 obj 中的name属性 设置为 数组 [1, 2, 3], 会执行set方法,会打印信息
        obj.name = [1, 2, 3];

        // 然后对 obj.name 中的某一项进行改变值,不会执行set方法,不会打印信息
        obj.name[0] = 11;

        // 然后我们打印下 obj.name 的值
        console.log(obj.name);

        // 然后我们使用数组中push方法对 obj.name数组添加属性 不会执行set方法,不会打印信息
        obj.name.push(4);
        
        //使用数组中unshift方法对 obj.name数组添加属性 不会执行set方法,不会打印信息
        obj.name.unshift(5)
        
         console.log(obj.name,"删除前");//删除前
        obj.name.pop() //删除obj.name数组最后一个元素
         console.log(obj.name,"删除后");//删除后,最后一个元素被删除了,但是set方法并没有执行,

        obj.name.length = 5; // 也不会执行set方法
        console.log(obj.name.length,obj.name[3])

下图是运行结果\

image.png

从运行结果可以看到,当我们改变数组某一项的值,给数组添加或删除属性、或改变数组长度,都没有执行set方法。也就是如果我们对数组中的内部属性值直接更改的话,都不会触发set方法。因此如果我们想实现数据双向绑定的话,我们就不能简单地使用 obj.name[1] = newValue这样的来进行赋值了。那么对于vue这样的框架,那么一般会重写 Array.property.push方法,并且生成一个新的数组赋值给数据,这样数据双向绑定就触发了。

问题2、 无法监听 属性的添加和删除\

在vue2中使用中你可能遇到过这样的问题

# template
<div @click="add(obj.a)">{{ obj.a }}</div>
<div @click="addb(obj.b)">{{ obj.b }}</div>

# srcript
data () {
    return {
        obj:{
            a:1
        }
    }
},
mounted () {
    this.obj.b = 1; //给对象新增属性b
},
methods: {
    addb(item){
        item += 1;
        console.log(this.obj)
    }
}

当点击obj.a是响应式, 页面也会更新
而点击新增的obj.b则不会更新。
所以Object.defineProperty无法监听到新增的对象属性,删除也一样\

针对这个问题vue2的解决方案:

新增对象属性$set

Vue.set(object, 'key', value) //第一个参数是添加属性的对象,第二个参数是要添加的属性,第二个参数是要添加的属性的值
this.$set(object, 'key', value)
//上面两种写法都一样

this.obj = Object.assign({},this.obj, { b: 1, e: 2 })
this.obj = {...this.obj,...{ b: 3, e: 2 }}

数组解决方案类似 关于vue无法侦听数组及对象属性的变化的解决方案

删除对象属性 $delete

    Vue.delete(target,'object')//第二个参数是字符串[也就是我们要删除的属性]
    this.$delete(target,'object')

问题3、 必须遍历对象的每个属性, 必须深层遍历嵌套的对象。

image.png Object.defineProperty是对对象属性的操作,所以需要对对象进行深度遍历去对属性进行操作。
使用 Object.defineProperty() 多数要配合 Object.keys() 和遍历,,于是多了一层嵌套.

image.png

Object.keys(obj).forEach(key => { 
    Object.defineProperty(obj, key, { 
        // ... 
    }) 
})

当一个对象为深层嵌套的时候,必须进行逐层遍历,直到把每个对象的每个属性都调用 Object.defineProperty() 为止。

简单通俗的理解Vue3.0中的Proxy
vue2.x实现数据的响应部分源码
Proxy使用详解
深入理解 Object.defineProperty 及实现数据双向绑定
vue2数据响应原理核心 Object.defineProperty() 深入浅出的讲解
# Object.defineProperty与Proxy理解整理