动态数据绑定是MVVM框架中最基础的的一个功能,简单描述就是:将数据和视图进行绑定,当数据发生改变时,视图随之改变,更深层次一点,数据绑定包括单向数据绑定和双向数据绑定。
本文从数据绑定中的问题出发,一步一步的来实现这个功能。
问题一
给定任意一个对象,如何监听其属性的读取与变化?也就是说,如何知道程序访问了对象的哪个属性,又改变了哪个属性?
举个例子:
实现这样的一个Observer并不难,在此我们暂且不考虑数组的情况,只针对传入的参数为对象。如果对ES6和ES5都熟悉的话,可以立刻想到针对上述场景,可以有两种的实现方式:
- 采用ES6中的proxy,对目标对象的属性进行拦截处理
- 采用ES5中的defineProperty,为目标对象的属性添加setter和getter
ES6中的proxy方法详细介绍可看阮一峰老师的《ECMAScript 6入门》地址:http://es6.ruanyifeng.com/#docs/proxy
第一种实现方式:
第二种实现方式:
问题二
如果传入的参数对象是一个“比较深”的对象(也就是其属性值也可能是对象),那该怎么办?
举个例子:
基于问题一的第二种实现方式做改进:
问题三
如果设置新的值是一个对象的话,新设置的对象的属性是否能继续响应getter和setter呢?
举个例子:
基于问题一的第二种实现方式,在setter中做相应的改进:
问题四
考虑传递回调函数。在实际应用中,当特定数据发生改变的时候,我们是希望做一些特定的事情,而不是每一次只能打印出来一些信息,所以,我们需要支持传入回调函数的功能。
举个例子:
针对上述场景,我们需要实现$watch这个API,每当年龄发生改变的时候触发相应的回调函数。这个API的实现可以很有多种方式,在此我们采用事件的方式来实现,通俗的讲就是实现一个通用的事件模型,每次$watch一个属性相当于注册了一个监听事件,当属性发生改变的则触发对应的事件,这样做的优势是可以为同一个属性通过事件模型来注册多个回调函数。
下边是一个不完整的简易事件模型:
有了上述事件模型后,每次new一个Observer的实例时,就new一个Event实例出来用来管理Observer实例中的所有事件;然后通过$watch API来为Observer实例注册属性的监听事件,每次当属性改变的触发相应的事件队列。
总结一下本文仍然需要解决的问题:
- 当传入的参数为数组时,如何监听数组对象的变化
- 深层对象属性的事件回调监听,或者描述为:对象的深层属性值发生变化后如何向上传递到顶层
- 动态数据与视图的绑定,如何绑定,当数据变化后如何触发视图的自动刷新
- 问题四中只可以监听对象的第一层属性,对于深层次的属性并不能有效监听