1. vue 2 响应式原理
Vue 数据双向绑定时通过数据劫持
结合 订阅者-发布者
模式 来实现的(观察者模式)。
- 数据劫持: 通过
Object.defineProperty(obj, key, value)
方法给对象的每一个属性都加上一个getter
和setter
(监听的是每一个属性)。 - 订阅者-发布者: 当我们修改某个属性的值时,底层调用了
setter
修改数据,当数据发生变化会被vue实例
监听到,从而调用相应的getter
方法,获取新数据,实现数据双向绑定。 - 再通过
deff
算法将数据更新到页面。(多说一句,react是单向数据绑定,就只是通过deff算法将数据更新到页面)
1.1 对象数据响应式原理
- 通过
Object.defineProperty(obj, key, value)
来监听已有的数据,当数据发生变化会调用响应的setter
和getter
Object.defineProperty()
针对的是对象的某个属性,而且这个操作在vue的初始化阶段就完成了,所以新增和删除的属性无法监听,通过Vue.set(obj, newkey, newvalue)
和Vue.delete(obj, key)
方法来解决,通过set()和delete()方法新增对象就相当于初始化阶段的数据响应式处理。
1.2 数组数据响应式原理
vue 对JavaScript数组的方法进行了二次封装(重写)来劫持这些方法,在原有操作数据的基础上,添加了将数据响应到页面的功能。
缺点:
- 新增、删除 数组里的数据,不会引起页面变化。
- 通过索引修改数据,不会引起页面变化。
- 因为getter和setter只能监听到数据的访问和修改动作,删除和添加动作无法监听到。
为什么监听不到呢?
- Vue 出于对性能的考虑,数组没有使用
Object.defineProperty
对属性添加setter
和getter
bject.defineProperty()
是可以对数组实现监听操作的,但是vue并没有实现这个功能,因为数组长度不定而且数据可能会很多,如果对每一个数据都实现监听,性能代价太大。
但是注意:数组中的元素是引用类型时是会被监听的
解决方案:
- 添加数据:
this.$set(obj, newkey, newvalue)
或Vue.set(obj, newkey, newvalue)
- 删除数据:this.$delete(obj, key) 或 Vue.delete(obj, key)
this.$delete
:只是被删除的数组成员变为 empty或undefined,其他元素的键值不变Vue.delete
:直接删除了数组的成员,并且改变了数组的键值(对象是响应式的,确保删除能触发更新视图,这个方法主要用于避开 Vue 不能监听到属性被删除的限制)
2. Vue 3 响应式原理
2.1 vue 2 缺陷
vue 2 通过设定对象属性getter/setter方法来监听数据的变化,同时getter也用于依赖收集,而setter在数据变更时通知订阅者更新视图。
2.2 新的代理方式 Proxy
Proxy,字面意思是代理,是ES6提供的一个新的API,用于修改某些操作的默认行为,可以理解为在目标对象之前做一层拦截,外部所有的访问都必须通过这层拦截,通过这层拦截可以做很多事情,比如对数据进行过滤、修改或者收集信息之类。
所以只需一层代理就可以监听统计结构下所有属性变化,包括新增和删除属性。
- Proxy直接代理整个对象而非对象属性:
- Proxy的代理针对的是整个对象,而不是像Object.defineProperty针对某个属性。只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性
- Proxy也可以监听数组的变化。