Vue双向绑定原理及实现
字数统计:1.3k字目录
双向绑定方法
发布者-订阅者模式(backbone.js
)
脏值检查(angular.js
)
数据劫持(vue.js
)
发布者-订阅者模式: 一般通过sub, pub
的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set('property', value)
脏值检查: angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,最简单的方式就是通过 setInterval()
定时轮询检测数据变动,当然Google不会这么low,angular只有在指定的事件触发时进入脏值检测,大致如下:
DOM事件,譬如用户输入文本,点击按钮等。( ng-click
)
XHR响应事件 ( $http
)
浏览器Location
变更事件 ( $location
)
Timer事件( $timeout
, $interval
)
执行 $digest()
或 $apply()
数据劫持:
vue.js
则是采用数据劫持结合发布者-订阅者模式
的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调。
Vue
响应系统,其核心有三点:observe
、watcher
、dep
:
observe
:遍历 data
中的属性,使用 Object.defineProperty
的 get/set
方法对其进行数据劫持;
dep
:每个属性拥有自己的消息订阅器 dep
,用于存放所有订阅了该属性的观察者对象;
watcher
:观察者(对象),通过 dep
实现对响应属性的监听,监听到结果后,主动触发自己的回调进行响应。
实现虚拟DOM包含以下三个步骤:
用JS对象模拟DOM树
比较两棵虚拟DOM树的差异, Diff算法
映射成真实DOM
Vue 双向数据绑定的原理
Object.defineProperty()
属性描述符有两种主要形式:数据描述符
和存取描述符
。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。Object.defineProperty
是ES5
新增的一个API
,其作用是给对象的属性增加更多的控制Object.defineProperty(obj, prop, descriptor)
参数 :
obj
: 需要定义属性的对象(目标对象)
prop
: 需被定义或修改的属性名(对象上的属性或者方法)
对于setter
和getter
,我的理解是它们是一对勾子(hook
)函数,当你对一个对象的某个属性赋值时,则会自动调用相应的setter
函数;而当获取属性时,则调用getter
函数。这也是实现双向数据绑定的关键。
descriptor
: 将被定义或修改的属性描述符。
描述:
该方法允许精确添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in
或 Object.keys
方法), 这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。默认情况下
,使用 Object.defineProperty()
添加的属性值是不可修改
的。
整理思路
实现mvvm的双向绑定,就必须要实现以下几点:
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
4、mvvm入口函数,整合以上三者
observer用来实现对每个vue中的data中定义的属性循环用Object.defineProperty()
实现数据劫持,以便利用其中的setter和getter,然后通知订阅者,订阅者会触发它的update方法,对视图进行更新。1
2
3
4
5
6
7我们介绍为什么要订阅者,在`vue`中`v-model`,`v-name`,`{{}}`等都可以
对数据进行显示,也就是说假如一个属性都通过这三个指令了,那么每当这个属性
改变的时候,相应的这个三个指令的html视图也必须改变,于是vue中就是每当有
这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是
更新自己的指令对应的数据,也就是`v-model='name'`和`{{name}}`有两个对
应的订阅者,各自管理自己的地方。每当属性的set方法触发,就循环更新Dep中
的订阅者。
Object.defineProperty
缺陷:
只能对属性进行数据劫持,对于JS对象劫持需要深度遍历;
对于数组不能监听到数据的变化,而是通过一些hack办法来实现,如push、pop、shift、unshift、splice、sort、reverse