MVVM的实现原理入门了解2-- 数据绑定初始化
数据绑定主要应用文件为 observer.js
因为 在MVVM文件中 数据代理后 执行的代码为
Object.keys(data).forEach(function(key) {
me._proxyData(key);
});
observe(data, this);
因此 数据绑定为第二步
observer.js的内容
//数据绑定
function Observer(data) {
this.data = data; //将data 保存到 Observer中
this.walk(data); //执行walk并将data传入
}
Observer.prototype = {
// 执行
walk: function(data) {
var me = this; // 使用me替换this
Object.keys(data).forEach(function(key) { //将data中的属性名组成数组并遍历
me.convert(key, data[key]); //调用convert方法 传入 data的属性名与属性值
});
},
convert: function(key, val) {
//定义响应式 传入Observer的data 以及 data中的属性名与属性值
this.defineReactive(this.data, key, val); //实现数据绑定 也就是实现 defineProperty 数据劫持
},
// 使用数据劫持实现数据绑定的关键代码
defineReactive: function(data, key, val) {
//在数据劫持时 创建Dep dependency(依赖)
// dep中的个数又 data中的属性个数确定 且与data的属性一一对应 (包括属性为对象中的属性)
// data:{ name:"zh",option:{ a:"a",b:"b" }} 此时dep数量为4个 分别为name option a b 等对应
var dep = new Dep();
var childObj = observe(val); // 如果data中的属性为对象 那么递归调用observe 直到不是对象
Object.defineProperty(data, key,{
enumerable: true, // 可枚举
configurable: false, // 可配置性为false 那么不能在进行删除、重写等操作
get: function() {
// 建立dep与watcher关系的代码
if (Dep.target) { //Dep中的target默认为null
dep.depend();
}
return val;
},
//setter 主要用来更新界面
set: function(newVal) {
if (newVal === val) {
return;
}
val = newVal;
// 新的值是object的话,进行监听
childObj = observe(newVal);
// 通知订阅者 也就是通知dep中 subs中存入的watcher
dep.notify();
}
});
}
};
function observe(value, vm) {
// 判断data 不存在或者 不为一个对象 结束执行
if (!value || typeof value !== 'object') {
return;
}
// 返回Observer实例 传入 data
return new Observer(value);
};
var uid = 0;
function Dep() {
this.id = uid++; //标识
this.subs = []; //subs --> Subscribe 订阅/订阅者 创建订阅者数组(存放的watcher)
}
Dep.prototype = {
addSub: function(sub) { //watcher的添加
this.subs.push(sub); //将subs数组中 添加对应的watcher
},
// 调用将addDep的this指向为 Dep.target 也就是说 watcher调用addDep
// 建立dep与watcher之间关系的代码
depend: function() {
Dep.target.addDep(this);
},
removeSub: function(sub) {
var index = this.subs.indexOf(sub);
if (index != -1) {
this.subs.splice(index, 1);
}
},
notify: function() {
// 遍历subs中的数据 然后为每一个sub调用update更新 因为subs中存入的是每一个watcher 因此调用每一个watcher对应的update方法
this.subs.forEach(function(sub) {
sub.update();
});
}
};
Dep.target = null;
var childObj = observe(val);
具体每一步都进行了注释 现在 解释下
在observer.js中的执行流程
- 在MVVM中调用observe(data,this) --> 因为data为一个对象因此 new Observer(data)
- 将data保存在observer函数上 调用walk并传递data
- walk方法地主要功能为 遍历data中的属性值与属性名 并传递给实现数据绑定的关键方法defineReactive 省略convert
- defineReactive的作用就是使用数据劫持实现数据绑定 defineReactive中
- new Dep(); 稍后讲
- var childObj = observe(val); 这一步的作用就是将传递的data中的属性值进行递归遍历,查看是否为对象,跳出递归的条件为data中的属性值不为对象时 如 data:{ name:"中国",city:{ north:"北京" } } 目的就是 将data中的name属性进行数据劫持、对city中的north属性进行数据劫持
- 使用object.defineProperty 实现数据劫持 主要就是为data中的每一个属性名添加set与get方法
- set方法 值发生改变时调用
set: function(newVal) { if (newVal === val) { return; } val = newVal; childObj = observe(newVal);//判断这个新的值是否为对象 如果是则进行监视 dep.notify();//通知订阅者 } - get方法 查询这个值时调用
get: function() { if (Dep.target) { //由于此时Dep.target = null 因此不执行该代码 dep.depend(); } return val; }
- set方法 值发生改变时调用
对于Dep的理解
dep中有两个属性 一个为id数字,一个为subs数组 在observer.js文件中有一个
var uid = 0;
function Dep() {
this.id = uid++;
this.subs = [];
}
因此 每一次new Dep id都会被加1 从零开始 而 执行new Dep的代码 是在defineReactive进行
defineReactive方法是为每一个data中的属性进行绑定set与get事件 由此可以指定 dep的id就是data中每一个属性的唯一标识
比如
data: {
someStr: 'hello ',
htmlStr: '<span style="color: #f00;">red</span>',
child: {
someStr: 'World !'
}
},
那么 someStr对应的dep.id为0 htmlStr对应的dep.id为1 child对应的dep.id为2 child.someStr对应的dep.id为3
目前observer初始化所作的全部内容 由于Dep.target = null 所以dep.depend()不执行 dep.notify()因为subs数组为空 所以也不执行

浙公网安备 33010602011771号