Object.defineProperty()
这个API是实现双向绑定的核心,最主要的作用是重写数据的get
、set
方法。
使用方法:
1 | let obj = { |
实现思路
- 实现数据监听器
Observer
,用Object.defineProperty()
重写数据的get
、set
,值更新就在set
中通知订阅者更新数据。 - 实现模板编译
Compile
,深度遍历dom
树,对每个元素节点的指令模板进行替换数据以及订阅数据。 - 实现
Watch
用于连接Observer
和Compile
,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图。流程图
代码实现
html结构
1
2
3
4
5
6
7<div id="wrap">
<p v-html="test"></p>
<input type="text" v-model="form">
<input type="text" v-model="form">
<button @click="changeValue">改变值</button>
{{form}}
</div>
JS调用
1 | new Vue({ |
Vue结构
1 | class Vue{ |
- Vue constructor 构造函数主要是数据的初始化
- proxyData 数据代理
- observer 劫持监听所有数据
- compile 解析dom
- compileText 解析dom里处理纯双花括号的操作
- Watcher 更新视图操作
Vue constructor 初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14class Vue{
constructor(options = {}) {
this.$el = document.querySelector(options.el);
let data = this.data = options.data;
// 代理data,使其能直接this.xxx的方式访问data,正常的话需要this.data.xxx
Object.keys(data).forEach(key => {
this.proxyData(key);
});
this.methods = options.methods; // 事件方法
this.watcherTask = {}; // 需要监听的任务列表
this.observer(data); // 初始化劫持监听所有数据
this.compile(this.$el); // 解析dom
}
}
proxyData 代理data
1 | class Vue{ |
上面主要是代理data到最上层,this.xxx的方式直接访问data
observer 劫持监听
1 | class Vue{ |
同样是使用Object.defineProperty来监听数据,初始化需要订阅的数据。
把需要订阅的数据到push到watcherTask里,等到时候需要更新的时候就可以批量更新数据了。
compile 解析dom
1 | class Vue{ |
首先我们先遍历el元素下面的所有子节点,node.nodeType === 3 的意思是当前元素是文本节点,node.nodeType === 1 的意思是当前元素是元素节点。因为可能有的是纯文本的形式,如纯双花括号就是纯文本的文本节点,然后通过判断元素节点是否还存在子节点,如果有的话就递归调用compile方法。
Watcher
1 | class Watcher { |