發佈/訂閱者模式
要搞清楚vue中的雙向數據綁定原理,就必須理解什麼是發佈、訂閱者模式!!
1、首先想好誰是發佈者、誰是訂閱者(例如NBA專欄就是發佈者,NBA球迷就是訂閱者)
2、然後給發佈者添加一個緩存列表,用於存放回調函數用來通知訂閱者
3、最後就是發佈消息,發佈者者遍歷這個緩存列表,依次觸發訂閱者的回調函數
廢話不多說,直接上代碼!希望大家能讀懂源碼再繼續往下看vue的數據更新原理
let NBAcol = {}; // 自定義一個NBA專欄對象(這就是發佈者)
NBAcol.list = []; // 這裏用一個列表來緩存訂閱者的回調函數
NBAcol.on = function(key, fun) {// on方法是給list數組添加相應的回調函數
// 如果還沒有訂閱此類消息,給該類消息創建一個緩存列表
/**
* list:["xiaoming": [fun], "xiaoqiang": [fun]]
*/
if(!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fun); // fun就是通知訂閱者的回調函數
}
// 發佈事件
NBAcol.emit = function(key, value) {
let funs = this.list[key] // 取出list中對應key的用於通知的回調函數數組
if(!funs || funs.length === 0){// 如果沒有訂閱過消息,直接返回
return;
}
funs.forEach(fn => {
fn(value)
});
}
// 取消事件的訂閱
NBAcol.remove = function(key, fun) {
let funcs = this.list[key];
if(!funcs) {
return false
}else {
funs.forEach((item, index) => {
if(item === fun) {
funcs.splice(index, 1)
}
})
}
}
// 小明的訂閱NBA專欄
NBAcol.on("xiaoming",function(team) {
console.log("小明訂閱的球隊是:" + team);
})
// 小強的訂閱NBA專欄
NBAcol.on("xiaoqiang", function(team) {
console.log("小強訂閱的球隊是:" + team);
})
NBAcol.emit("xiaoming",'湖人');
NBAcol.emit("xiaoqiang", '勇士');
// 取消訂閱
NBAcol.remove('xiaoli',function(team){
console.log("我訂閱的球隊是:"+team)
});
理解了什麼是發佈,訂閱者模式之後我們再來看看vue中更新數據原理。
現象:數據有變化,相關依賴項也會跟着變化
實現:把依賴項的方法(回調函數)封裝在Dep依賴項裏面,數據變化時下發通知Dep重新執行相關方法
let data = {price: 5, quantity: 2};
let target = null;
class Dep{
constructor() {
this.subscribers = [];
}
depend() {
if(target && !this.subscribers.includes(target)) { // target就是對數據的一些操作,即添加訂閱者
this.subscribers.push(target)
}
}
notify() {
this.subscribers.forEach(sub => sub())
}
}
//使用Object.defineProperty來更新data裏面的數據
Object.keys(data).forEach(key => {
let internalValue = data[key];
const dep = new Dep;
Object.defineProperty(data, key, {
get(){
dep.depend();
return internalValue;
},
set(newValue) {
internalValue = newValue;
dep.notify();
}
})
})
function watcher(myFunc) {
target = myFunc;
target();
target = null;
}
watcher(() => {
data.total = data.price * data.quantity;
})
data.price = 200;
console.log(data.total);
總結:
1、首先建立一個Dep依賴項(類):在Dep類裏面定義一個subscrers數組用於收集訂閱者的回調函數(depend())並且逐個執行(notify());
2、創建數據的劫持: 使用Object.keys(data);遍歷data的key,然後通過Object.defineProperty(data, key, {...})來更新data裏的數據; 並在遍歷中實例化Dep且在get中執行dep.depend(),在get()中執行dep.notify(),注意這裏是更新後再執行!
3、最後就是定義一個監聽函數watcher()了;該函數參數是一個函數,把該函數賦值給target並執行之後置爲null,taget就是一個訂閱者的回調函數