Reactive Programming是一种编程形式,在很多场景都会见到,最近正在学习的RxJS是一个例子,当然Vue同样是一种Reactive Programming,就是当变量发生改变的时候,相关的变量和视图也会跟着改变,而我们开发者不需要自己去写代码来实现这个过程,我们只需要关心变量改变之后应该进行什么操作,更加关注于业务流程。
Vue的双向数据绑定是基于ES5的Object.defineProperty()的getter和setter,每当数据发生变化,就会执行getter/setter,结合发布者/订阅者的模式,通知订阅者这些变化,进而执行相应的回调函数。
今天我们来分析一下vue双向数据绑定的原理,同时我们自己用js来实现一个简单的双向数据绑定,首先看一下原理图:

MVVM就是我们要实现的vue实例,简单讲述一下流程:
- 首先通过一个
Observer(监听器或者劫持器)去劫持data对象中的所有属性,方法就是使用Object.defineProperty()中的getter/setter,在属性set的时候通知Dependency(订阅器/容器)发布变化; - 实现一个
Watcher(订阅者),这个Watcher就是说我收到数据变化的通知后,应该去执行什么操作(重新填充列表,填充值等等,即更新视图),一个data.message数据可能对应多个使用场景,比如v-model="message"、v-text="message"、{ {message} }等等,所以Watcher不止一个; - 上面说到
Watcher不止一个,所以我们可以实现一个容器Dependency,里面存放data.message对应的所有Watcher,这样当Observer的Setter改变时,调用Dependency的notify方法,逐条去通知所有的Watcher; - 实现一个编译器
Complier,编译器的作用是扫描和解析每一个节点node,先将节点转换为fragment(性能优化,一次性append所有节点至目标element内),再根据不同的节点类型nodeType,针对v-model、v-text、做不同的处理,完成第一次的数据message填充(即初始化视图);同时编译器还担当着初始化Watcher的任务,将Watcher添加到Dependency中去;
有了以上的思路,接下来就是编写代码时间,使用了ES6的class,首先我们来实现Observer:
1 | class Observer { |
接下来实现Watcher:
1 | class Watcher { |
然后我们需要一个容器Dependency去储存data.message对应的所有Watcher:
1 | class Dependency { |
下面是编译器Complier,编译器涉及的东西比较杂,判断的情况比较多,所以这里只考虑到了v-model、v-text、{ {message} }这3种情况的实现:
1 | class Complier { |
还有入口main.js:
1 | class MVVM { |
最后就是html调用了:
1 |
|
总结:实例化MVVM时,先使用Object.defineProperty劫持每一个data数据,为每一个属性实例化一个Dependency;在编译页面的时候为每一个需要更新message的地方添加一个Watcher,即v-model="message"、v-text="message"和{ {message} },有一个算一个,将这些Watcher添加到Dependency中进行统一管理;在编译的时候我们还要为input添加一个事件监听addEventListener,这样input的输入值变化时,触发setter,在setter内调用Dep的notify()方法,循环调用每一个Watcher的update更新我们的视图(执行回调函数)。
以上代码都放到了我的github仓库vue-principle,欢迎查阅。
参考文章:vue双向绑定原理分析,vue的双向绑定原理及实现