在上一篇学习的时候对观察者模式和发布-订阅者模式进行了更深层次的学习;这里通过一个 input 模拟VUE双向绑定(正好学习)的功能来加深对观察者模式的记忆,这个例子只对 v-model 指令及显示文本的模板语法进行处理来进行学习(Vue中有很多其它指令等,不做处理)。
1 | <div id="app"> |
注意:下文中 \{\{\}\}
这个符号标识的是 Mustache
语法
思路
input => 数据:给input加一个事件,当变化时让其绑定的数据及时变化
数据 => input:通过 defineProperty 设置 get 和 set 来进行数据劫持,触发视图的更新
代码实现
给所有数据都用 defineProperty 设置 get,set。
1 | function observer(data) { |
这样,对所有的数据都进行了劫持,只要数据有修改那么在 set 中都能够监听到。
MVVM 构造函数
调用构造函数 MVVM 的时候,需要把 data 里面的所有数据的键值都绑定上 get 和 set;然后再编译模板。
1 | class MVVM { |
模板编译
既然在构造函数 MVVM 创建实例的时调用了 Compile 函数对模板进行编译。那么模板编译到底是干什么?
其实模板编译就是遍历节点,寻找具有 v-model 属性的元素节点,以及 \{\{\}\}
文本模板语法这种格式的文本节点。
1 | class Compile { |
这里查到了对应的属性 v-model,只要一个数据变化,跟它所关联的 dom 元素都需要更新;
答案出来了,已经是上一篇学习的观察者模式了,观察者(observer)会被添加到目标(Subject)中,目标一通知,所有的观察者都会更新。所以在检测到属性 v-model
和 \{\{\}\}
后需要创建一个观察者,添加到目标中去。
实现观察者
在观察者里面需要实现一个 update 方法对监测到数据改动时的更新。
1 | let uId = 0; |
实现目标(Subject)
观察者是被添加到目标上的,所以得有一个目标构造函数。
1 | class Dep { |
通过前面的线索整理最后结果
- 首先,在给 key 值添加 get,set 的时候都要创建一个 Dep(目标)。
- 再次是编译模板时,遇到
v-model
或者\{\{\}\}
就要创建一个观察者添加到 Dep,同时将 data 里面的值赋给 当前节点,并且 input 还需要绑定一个 input 事件,输入时改变对象里面的值。 - 在观察者中准备一个全局变量,利用 key 值的 get 属性将它添加到 watcher。
- 最后,将所有思路整理成 最终代码