前篇講到compile.js執行進行模板解析 同時new Watcher實例化watcher
由於watcher中在初始化時 使用 this.value = this.get()方法
get: function() {
Dep.target = this;
var value = this.getter.call(this.vm, this.vm);
Dep.target = null;
return value;
},
- get方法的調用使 Dep.target = watcher(也就是data中的屬性解析模板時創建的watcher)
- 然後調用var value = this.getter.call(this.vm,this.vm) 進行取值
- 取對應data中屬性名的屬性值 由於observer對data中的每一個屬性都進行了數據劫持添加了get與set方法
- 由於是取值執行get方法
get: function() { if (Dep.target) { //Dep中的target此時爲watcher dep.depend(); //執行此代碼 } return val; },
depend: function() { Dep.target.addDep(this); },
由於此時Dep.target 爲watcher 因此 調用watcher.addDep(this) this此時爲data屬性名對應的dep
-
執行addDep
addDep: function(dep) { //判斷watcher與dep的關係是否建立 防止同一個data中的屬性對應的dep與對應的watcher重複建立關係 if (!this.depIds.hasOwnProperty(dep.id)) { dep.addSub(this); //調用dep的addSub方法 傳入 watcher this.depIds[dep.id] = dep; //讓depIds容器中的dep.id屬性名的屬性值爲 dep // depIds:{ dep.id:dep } 此時也就是將 dep保存到了 watcher中 } },
由於depIds存儲的是dep 而dep屬性中又有id與subs 兩個屬性 因此
-
dep.addSub(將當前的watcher傳遞給dep 並將watcher保存到dep中的subs中)
-
將當前的dep保存到watcher中的depIds中去 以dep.id爲屬性名 dep爲屬性值
-
由於解析表達式才創建watcher 因此每次的watcher都是不同的 因此每一次的this.depIds爲空
-
-
addSub: function(sub) { //watcher的添加 this.subs.push(sub); //將subs數組中 添加對應的watcher },
因此這樣可以將每次不同的watcher完全的保存到對應的dep.subs中
比如
<p>{{someStr}}</p> <p v-text="someStr"></p>
那麼此時 watcher應爲2個(模板中解析指令創建watcher) dep爲1個(進行數據劫持時創捷dep) data:{someStr:"hello"} ---> 因此 depIds存入的爲 depIds:{ dep.id:dep } --> {dep.id:{dep.id,dep.subs}} --> { 0 : {0,[watcher,watcher] } }
-
初始化完畢返回val 也就是對應屬性的屬性值
walk: function(data) { var me = this; Object.keys(data).forEach(function(key) { me.convert(key, data[key]); }); }, convert: function(key, val) { this.defineReactive(this.data, key, val); }, defineReactive: function(data, key, val) { var dep = new Dep(); var childObj = observe(val); Object.defineProperty(data, key,{ enumerable: true, configurable: false, get: function() { if (Dep.target) { dep.depend(); } return val; } set:.... }
當執行更改data中屬性名對應的屬性值時 進行操作
當數據發生更改時 執行set方法
set: function(newVal) {
if (newVal === val) {
return;
}
val = newVal;
// 新的值是object的話,進行監聽
childObj = observe(newVal);
// 通知訂閱者 也就是通知dep中 subs中存入的watcher
dep.notify();
}
- 執行dep.notify()
notify: function() { this.subs.forEach(function(sub) { sub.update(); }); }
update: function() {
this.run();
},
run: function() {
// 讓value等於 this.get
var value = this.get(); //獲取最新的值
var oldVal = this.value;//獲取舊值
if (value !== oldVal) {// 如果新的值與舊的值不相等
this.value = value; //讓value爲新的值
// cb.call 是執行Watcher中的cb形參
// 調用回調函數
this.cb.call(this.vm, value, oldVal); //更新界面的回調函數
}
},
主要執行爲cb方法 執行watcher的回調函數 用於視圖更新
new Watcher(vm, exp, function(value, oldValue) {
// 回調函數 用於更新data中屬性名對應的屬性值時 節點發生改變
updaterFn && updaterFn(node, value, oldValue);
});
MVVM的原理圖 前3張爲https://github.com/DMQ/mvvm作者提供
最後一張爲個人理解