Vue3.0时代你必须了解的:响应式原理
Vue3.0已经发布了,更新了一系列东西,其中包括用「Proxy」取代 「Object.defineProperty」。今天我们就开门见山,直接来聊聊关于新的响应式的原理和优点。
回顾Vue2.x 响应式原理
核心API --- Object.defineProperty()
1.Object.defineProperty()语法说明
Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
Object.defineProperty(obj, prop, desc)
- obj 需要定义属性的当前对象
- prop 当前需要定义的属性名
- 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 响应式的优点
那么,除了以上说的能直接监听数组变化的优点以外,还有什么优点呢?
- 选择性监听
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 排版