Vue2和Vue3响应式区别和理解

一、Vue2响应式

vue2响应式:核心代码是使用Object.defineProperty()来劫持对象中每一个属性的set和get方法。

先来说说Object.defineProperty,称之为对象的属性描述符,可以设置对象的属性是否可以删除(Configurable)、是否可以枚举(Enumerable)、是否可以修改(Writable)、返回值(value)、set(重要,修改触发的回调函数)、get(重要,获取触发的回调函数)

举例子:

var obj = {
     name: '小明',
     age: 18
   }

   Object.defineProperty(obj, "name", {
      set(newValue) {
       console.log('调用set方法', newValue);
       name = newValue
      },
      get() {
        console.log('调用get方法');
        return name
      }
   })

   Object.defineProperty(obj, "age", {
      configurable: false,
      enumerable: true
   })

   // 调用set
   obj.name = 123
   // 调用get
   console.log(obj.name)
   // 尝试删除age
   delete obj.age
   console.log(obj.age);

这是Object.defineProperty的基本用法,也是Vue2的响应式核心,但是,这么做是有缺点的。

缺点一:如果对象中属性过多,那么需要给每个属性都绑定defineProperty,十分损耗效率。

缺点二:只能监听对象属性的修改、读取,如果涉及删除、增加就无法响应式刷新页面了(只能通过Vue.set)

   this.$set(this.obj, key, value)
   Vue.set(this.obj, key, value)
    
   this.$delete(this.obj, key)
   Vue.delete(this.obj, key)

缺点三:通过下标、length修改数组也不会响应式刷新页面,可以直接重新赋值,或者使用filter、map、concat、slice等生成新数组对其赋值

   this.$set(this.arr, index, value)
   Vue.set(this.arr, index, value)
    
   this.$delete(this.arr, index)
   Vue.delete(this.arr, index)

还可以使用操作数组的函数

splice(),push(), pop(), shift(), unshift(), sort(), reverse()

好了,缺点就这些,下面我们来浅聊一下Vue2响应式是如何实现的。

顺序是:数据劫持(数据代理)、依赖收集 、订阅发布

首先observe函数会将传入的数据进行递归处理成一个个值,然后给他们每个都重写get和set方法。

然后Dep类作为收集者,需要保存收集到的依赖,在observe函数执行同时创建Dep实例,也就是每个属性对应一个Dep实例,在Vue解析到模板中的{{}}时,会创建Watcher实例,也就是观察者,可以收集数据的所有依赖,通过调用dep.depend,存放到对应Dep实例的sub数组中。

最后当数据发生变化,就会触发Dep类中的dep.notify方法,通知所有依赖进行更新。

二、Vue3响应式

和Vue2不同的是,它的核心是es6的Proxy结合Reflect实现的,使用代理,可以不直接操作对象,这样就可以监听到所有对象的增删改查,同时也提升了效率。

还是先看看核心Proxy:

es6新增了Proxy类,顾名思义,就是代理的作用。如果我们想监听一个对象,就可以先创建代理对象,之后对对象的所有操作,都由代理对象完成,可以监听我们的所有操作。

Reflect是es6新增的另一个API,是一个对象,顾名思义是反射。提供了很多操作Object的方法,增加Reflect的原因是最开始ECMA设计时,没有考虑到未来会给Object设置很多方法,造成臃肿的现象,而且有些操作放到object里面并不合适,所以新增了这个API。另外在使用Proxy时,可以做到不操作原对象

二者结合:

  var obj = {
            name: 'xiaoming',
            age: 18
        }
        // 结合好处一:不再直接操作对象
        // 结合好处二:返回一个Boolean类型,可以进行判断
        var objProxy = new Proxy(obj, {
            set: function(target, key, newValue) {
                const isSuccess = Reflect.set(target,key,newValue)
                if(!isSuccess){
                    throw new Error(`set ${key} failure`)
                }
            }
        })

Vue3的响应式是将Vue2的Object.defineProperty替换成new Proxy+Reflect,其他思想大致是一样的。

你可能感兴趣的:(vue.js,javascript,前端)