Vue双向数据绑定的原理是基于发布者订阅者的模式,利用初始化的时候用Object.defineProperty劫持数据对象及其子对象的属性,为他们添加getter/setter,在触发setter的时候,就能收到通知,触发相应的更新函数,从而更新视图。
那么vue的数据绑定的缺点应该从原理去入手,
-
Vue只有在初始化的时候才会劫持数据对象的getter、setter
这就导致了我们必须在data选项中提前声明我们想要双向数据绑定的数据,无法添加根级别的响应数据。 -
Vue劫持数据对象的getter、setter是利用了Object.defineProperty这个函数,那么这个函数的缺点即是vue双向数据绑定的缺点。
Object.defineProperty无法监听到添加属性、删除属性等的操作,并且无法监听数组的变化(有资料显示Object.defineProperty可以监听数组的变化,但是vue中不对数组进行监听)。
Object.defineProperty只能劫持对象的属性,而不能劫持整个对象,因此需要对对象的所有属性进行遍历,如果属性也是对象的话,那么要进行深度的遍历。
表现为:
- 当我们给对象添加、删除属性的时候,vue是无法监听到变化的
- 直接用下标修改数组项的时候,不会被vue检测到
- 修改数组的长度的时候,也不会被vue检测到
Vue中只有七种数组操作的方法可以被检测到,分别是:push()、pop()、shift()、unshift()、splice()、reverse()、sort()。
Vue的实现是对数组的这几个操作进行了hack,使之能够变成响应的属性。
以上这几种方法也是数组方法中,会改变原数组的方法,其他方法不会。
对象添加响应式属性
为了弥补这种缺陷,可以用Vue.set()方法,改方法可以为对象添加响应式属性,在实例上可以用vm.$set(),或者可以使用Object.assign()方法返回一个新的对象。
Vue.set(obj, propertyName, value);
vm.$set(obj, propertyName, value);
obj = Object.assign({}, obj, {a:xxx})
数组的响应式检测
弥补数组的缺陷可以用Vue.set(),也就是vm.$set()方法,也可以用数组的原生方法splice();
Vue.set(arr, index, value);
vm.$set(arr, index, value);
arr.splice(index, 1, value);
proxy和Object.defineProperty
基于Object.defineProperty只能劫持对象的属性,因此我们需要对对象进行递归遍历。
而取代它的proxy有以下的优点:
- 可以劫持整个对象,并且返回新的对象
- 有13种劫持操作。核心
proxy对象在ES2015规范中被正式发布,用于定义基本操作的自定义行为。(比如属性操作、赋枚举,函数调用等)。 - Proxy是ES6的新特性,翻译过来是“代理”的意思,用在这里表示让他来代理某些操作。
proxy能够让我们以简洁明了的方式控制外部对对象的访问,类似于设计模式中的代理。 - Proxy可以理解为在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此可以用来对外界的访问进行过滤和改写。
proxy的优点是可以交由它来处理一些非核心的逻辑,比如说读取或者设置属性前记录日志,设置对象的属性前进行验证,某些属性的访问控制等。)从而让对象只关注核心逻辑,达到关注点分离,降低对象的复杂度。
proxy的基本用法
let p = new Proxy(target, handler);
target可以说任何类型的对象,包括原生数组、函数甚至另一个代理。
handler是一个对象,其声明了代理target的一些操作,其属性是当执行一个操作时,定义代理行为的函数。
p是代理之后的对象,每当对p有一些操作的时候,就会执行handler对象上的方法。Proxy有13种劫持操作,handler常用的代理操作如下:
get 读取操作
set 修改
has 判断对象是否有该属性
construct 构造函数
我们可以这样认为:proxy是Object.defineProperty的全方位加强版
Proxy有13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等,是Object.defineProperty所不具备的
Proxy做为新特性,将做为浏览器厂商持续优化的重点。
Proxy的缺点是浏览器兼容性的问题,并且无法用polyfill磨平(所以vue2.x才没有采用,3.0的时候采用了)
网友评论