Vue3.0时代你必须了解的:响应式原理

2,836 阅读4分钟

Vue3.0时代你必须了解的:响应式原理

Vue3.0已经发布了,更新了一系列东西,其中包括用Proxy取代 Object.defineProperty。今天我们就开门见山,直接来聊聊关于新的响应式的原理和优点。

回顾Vue2.x 响应式原理

核心API --- Object.defineProperty()

1.Object.defineProperty()语法说明

Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性

Object.defineProperty(obj, prop, desc)
  1. obj 需要定义属性的当前对象
  2. prop 当前需要定义的属性名
  3. desc 属性描述符
let Person = {}
let temp = null
Object.defineProperty(Person, 'name', {
  get: function () {
    return temp
  },
  set: function (val) {
    temp = val
  }
})
console.log(Person.name) // get()
Person.name = 'B' // set()

上面代码中我们可以看到,Object.defineProperty()的用法就是给一个对象定义一个属性(方法),并提供set和get两个内部实现,让我们可以获取或者设置这个属性(方法)

vue2.x的响应式原理就是利用这个api对数据的读取和赋值进行监听和拦截。 但是其中有个吐槽最多的地方就是:Vue2.x针对数组只实现了 push,pop,shift,unshift,splice,sort,reverse' 这七个方法的监听,以前通过数组下标改变值的时候,是不能触发视图更新的。 但是并不是因为Object.defineProperty()监听不了数组下标,因为数组也是对象的一种,只不过每个key都是数字罢了。之所以出现以上的原因主要是因为:

数组的多变性。数组可以通过下标直接操控数组长度,比如Array.length = 0;瞬间就将数组清空,Array.length = 100,瞬间为数组填充了100个空元素。也就是说数组的key和value都可能发生变化。一旦数组的下标发生变化,就要重新对数组进行递归遍历,使用 Object.defineProperty 加上 setter 和 getter,这在性能上是非常不友好的。

数组的操作方法太多了。但是通常 push,pop,shift,unshift,splice,sort,reverse 这 7 种操作就能达到目的。因此,出于性能方面的考虑 Vue2.x 做出了一定的取舍。

proxy

语法

const p = new Proxy(target, handler)

target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

handler一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

Vue3.0使用Proxy直接监听数据的各种自定义行为,免去了Object.defineProperty需要遍历的操作。上代码

const arr = ["2019","云","栖","音","乐","节"];
let ProxyArray = new Proxy(arr,{
    get:function(target, name, value, receiver) {
        console.log("取值")
        return Reflect.get(target,name);
    },
    set: function(target, name, value, receiver) {
       console.log("赋值")
       Reflect.set(target,name, value, receiver);;
    }
 })
 const index = ProxyArray[0];
 //取值
 ProxyArray[0]="2050"
 //赋值

以上代码实现的效果和 Object.defineProperty一样,而且免去了遍历对象的操作,可以说从性能上大大优化了,看起来也是一目了然。只要是对象,Proxy都能代理。

Vue3.0 响应式的优点

那么,除了以上说的能直接监听数组变化的优点以外,还有什么优点呢?

  1. 选择性监听

Vue3.0将响应式单独拆分出来,直接暴露给使用者使用,其中包括 ref、reactive

import {ref,reactive} from Vue

setup(){
  const count = ref(0)
  const person = reactive({name:'小红'})
}

在Vue3.0中我们将通过ref和reactive生成响应式的数据,也就是说Vue将选择权交给了使用者。我们将有选择性的让需要的数据‘响应’。

对比Vue2.x中,所有在模板中使用到的数据都需要在 data 中定义,组件实例在初始化的时候会将 data 整个对象变为可观察对象。这样的话,组件的实例化速度显而易见的会慢了一点,而且还需要开辟出部分运行内存,来保存这样多余的被‘响应化’的数据。

所以Vue3.0的响应式不但提高了组件实例初始化速度,而且还降低了运行内存的使用

IE 怎么办?

这是个实在不想提但又绕不开的话题,IE 在前端开发者眼里和魔鬼没什么区别。在 Vue3.0 之前,响应式数据的实现是依赖 ES5 的 Object.defineProperty,因此只要支持 ES5 的浏览器都支持 Vue,也就是说 Vue2.x 能支持到 IE9。Vue3.0 依赖的是 Proxy 和 Reflect 这一对出生新时代的 CP,且无法被转译成 ES5,或者通过 Polyfill 提供兼容,这就尴尬了。开发者技术前线获悉的信息,官方在发布最终版本之前会做到兼容 IE11,至于更低版本的 IE 那就只有送上一曲凉凉了。

其实也不用太纠结IE的问题,因为连微软自己都已经放弃治疗 IE 拥抱 Chromium 了,我们又何必纠结呢?

本文使用 mdnice 排版