vue数据双向绑定原理
目前几种主流的框架都实现了数据绑定,大致做法有:
数据劫持
要实现数据劫持,我们需要了解Object.defineProperty()方法。当我们创建一个普通属性时,可以看到属性描述符的各种性质的默认值,如果需要修改这些默认值,可以使用Object.defineProperty()方法
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。访问器描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
Vue就是通过Object.defineProperty()来实现数据劫持的。看下面这段代码:设置a属性时会触发set函数,读取a属性时会触发get函数
思路原理
MVVM数据双向绑定大致是两方面,一是视图改变更新数据,二是数据改变更新视图。视图更新数据通过事件监听实现,比如input标签监听’input’事件,在事件触发函数内修改数据;数据更新视图就运用到上节提到的Object.defineProperty()方法,当数据变化是会触发改方法内的set函数,然后在set函数内更新视图。要实现MVVM数据绑定,需要实现以下几点:
1.实现一个数据监听器Observer,用来监听数据对象上的所有属性,一旦数据变化就通知订阅者
2.实现一个订阅者watcher,订阅并收到属性变化的通知,然后执行绑定的回调函数更新视图
3.实现一个指令解析器,对每个元素节点上的指令进行扫描和解析,根据指令模板替换数据,以及绑定响应的回调函数
实现Observer
|
|
实现watcher
|
|
实现compile(后续补充)
Object.defineProperty的缺陷
数组漏洞
Object.defineProperty无法监听数组变化。
- 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:vm.items.length = newLength
作者对数组的8类方法进行了hack处理,使用它们可以实现视图更新。1234567push()pop()shift()unshift()splice()sort()reverse()
对象漏洞
Vue 不能检测对象属性的添加或删除,所以添加属性的时候需要使用推荐的vm.$set方法。数据劫持时,需要对对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历。
Proxy实现数据的双向绑定
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
Proxy可以直接监听对象而非属性
我们用Proxy改造上文中用Object.defineProperty实现的数据绑定
Proxy直接可以劫持整个对象,并返回一个新对象,不管是操作便利程度还是底层功能上都远强于Object.defineProperty。
Proxy可以直接监听数组的变化
当我们对数组进行操作(push、shift、splice等)时,会触发对应的方法名称和length的变化。
Proxy 支持的拦截操作有13种: