數據綁定主要應用文件爲 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數組爲空 所以也不執行