数据劫持与数据绑定
1、数据绑定(model==>View):
(1)、一旦更新了data中的某个属性数据, 所有界面上直接使用或间接使用了此属性的节点都会更新(更新)。
2、数据劫持
(1)、数据劫持是vue中用来实现数据绑定的一种技术。
(2)、基本思想: 通过defineProperty()来监视data中所有属性(任意层次)数据的变化, 一旦变化就去更新界面。
3、四个重要对象
(1)、Observer
用来对data所有属性数据进行劫持的构造函数。
给data中所有属性重新定义属性描述(get/set)。
为data中的每个属性创建对应的dep对象。
(2)、Dep(Depend)
data中的每个属性(所有层次)都对应一个dep对象。
创建的时机:
在初始化define data中各个属性时创建对应的dep对象。
在data中的某个属性值被设置为新的对象时。
对象的结构:
{
id, // 每个dep都有一个唯一的id
subs //包含n个对应watcher的数组(subscribes的简写)
}
subs属性说明:
当一个watcher被创建时, 内部会将当前watcher对象添加到对应的dep对象的subs中。
当此data属性的值发生改变时, 所有subs中的watcher都会收到更新的通知, 从而最终更新对应的界面。
(3)、Compile
用来解析模板页面的对象的构造函数(一个实例)。
利用compile对象解析模板页面。
每解析一个表达式(非事件指令)都会创建一个对应的watcher对象, 并建立watcher与dep的关系。
complie与watcher关系: 一对多的关系。
(4)、Watcher
模板中每个非事件指令或表达式都对应一个watcher对象。
监视当前表达式数据的变化。
创建的时机: 在初始化编译模板时。
对象的组成:
{
vm, //vm对象
exp, //对应指令的表达式
cb, //当表达式所对应的数据发生改变的回调函数
value, //表达式当前的值
depIds //表达式中各级属性所对应的dep对象的集合对象
//属性名为dep的id, 属性值为dep
}
(5)、 总结: dep与watcher的关系: 多对多
一个data中的属性对应对应一个dep, 一个dep中可能包含多个watcher(模板中有几个表达式使用到了属性)。
模板中一个非事件表达式对应一个watcher, 一个watcher中可能包含多个dep(表达式中包含了几个data属性)。
数据绑定使用到2个核心技术:
defineProperty()
消息订阅与发布
4、双向数据绑定
(1)、双向数据绑定是建立在单向数据绑定(model==>View)的基础之上的。
(2)、双向数据绑定的实现流程:
在解析v-model指令时, 给当前元素添加input监听
当input的value发生改变时, 将最新的值赋值给当前表达式所对应的data属性
<body>
<!--
1. 数据绑定
* 初始化显示: 页面(表达式/指令)能从data读取数据显示 (编译/解析)
* 更新显示: 更新data中的属性数据==>页面更新
-->
<div id="test">
<p>{{name}}</p>
<p v-text="name"></p>
<p v-text="wife.name"></p>
<button v-on:click="update">更新</button>
</div>
<!--
Dep
创建时间:初始化的给data的属性进行数据劫持时创建的
个数:与data中的属性一一对应
Dep的结构:
id: 标识
subs:[] n个相关的watcher对象的容器
Watcher
创建时间:初始化的解析大括号表达式与一般指令时创建
个数:与模板中表达式(不包含事件指令)一一对应
Watcher的结构:
this.cb = cb; // 用于更新界面的回调
this.vm = vm; // vm
this.exp = exp; // 对应表达式
this.depIds = {}; // 相关的n个dep的容器对象
this.value = this.get(); // 当前表达式对应的value
Dep与Watcher之间的关系:多对多关系
一个dep中对应多个watcher:同一个属性在模板中多次使用 {{name}} v-text="name"
一个watcher中对应多个dep:多层表达式 {{a.b.c}}
建立关系的方法:data中属性的get()方法
建立时间:初始化的解析模块中的表达式创建watcher对象时
vm.name = 'xxx' 执行流程:
1、data中的name属性值变化
2、调用name的set()方法
3、通知dep
4、通知所有相关的watcher
5、调用cb()回调函数
6、调用updater方法更新界面
-->
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
new MVVM({
el: '#test',
data: {
name: 'feifei',
wife: {
name: 'feifei2',
age: 28
}
},
methods: {
//更新数据
update () {
this.name = 'xinxin'
}
}
})
</script>
</body>
<body>
<div id="test">
<input type="text" v-model="msg">
<p>{{msg}}</p>
</div>
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
new MVVM({
el: '#test',
data: {
msg: 'hello'
}
})
</script>
</body>