觀察者模式(訂閱發(fā)布模式)
通過 訂閱-發(fā)布 (subscribe-publish) 模型嘿般,消除組件之間雙向依賴
消息的 發(fā)布者 (subject) 不需要知道 觀察者 (observer) 的存在
兩者只需要約定消息的格式(如何訂閱靴姿、如何發(fā)布),就可以通信
我們先看一段代碼
document.querySelector(".div").innerHTML="我是周杰倫"
document.getElementsByClassName("div")[0].innerHTML="我是周杰倫"
$(".div").html("我是周杰倫1")
這種操作DOM的寫法看起來也很簡單拐辽,沒什么大問題拣挪,但是如果有N多DOM的時候數(shù)據(jù)需要動態(tài)換,則這樣的代碼就非常難以維護俱诸,甚至取名字都會成為程序員頭疼的事情菠劝。所以有些人就想是不是可以數(shù)據(jù)驅(qū)動。數(shù)據(jù)變化反饋到DOM上乙埃,DOM變化反饋到數(shù)據(jù)上闸英,形成這樣的雙向數(shù)據(jù)流的格式會比現(xiàn)在的操作DOM會更加方便。
所以我們有了雙向數(shù)據(jù)流VUE 介袜,Angular,有了單向數(shù)據(jù)流的 CYCLE 和 REACT
雙向數(shù)據(jù)綁定(two-way data binding)
意味著 UI 層所呈現(xiàn)的內(nèi)容和 Model 層的數(shù)據(jù)動態(tài)地綁定在一起了出吹,其中一個發(fā)生了變化遇伞,就會立刻反映在另一個上。比如用戶在前端頁面的表單控件中輸入了一個值捶牢,Model 層對應該控件的變量就會立刻更新為用戶所輸入的值鸠珠;反之亦然,如果 Modal 層的數(shù)據(jù)有變化秋麸,變化后的數(shù)據(jù)也會立刻反映至 UI 層渐排。
單向數(shù)據(jù)流(one-way data flow)
意味著只有 Model 層才是單一數(shù)據(jù)源(single source of truth)。UI 層的變化會觸發(fā)對應的消息機制灸蟆,告知 Model 層用戶的目的(對應 React 的 store)驯耻。只有 Model 層才有更改應用狀態(tài)的權(quán)限,這樣一來炒考,數(shù)據(jù)永遠都是單向流動的可缚,也就更容易了解應用的狀態(tài)是如何變化的。
言歸正傳 那么我們的訂閱發(fā)布到底是什么呢
其實就是字面意思 你訂閱了我的信息 等我更新了 我就給你一個反饋
大家都知道 VUE中有一個事件機制 業(yè)就是 $ON 和 $EMMIT 下面我們來看看 分析分析
$on
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
/*如果是數(shù)組的時候斋枢,則遞歸$on帘靡,為每一個成員都綁定上方法*/
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.$on(event[i], fn)
}
} else {
(vm._events[event] || (vm._events[event] = [])).push(fn)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
/*這里在注冊事件的時候標記bool值也就是個標志位來表明存在鉤子,而不需要通過哈希表的方法來查找是否有鉤子瓤帚,這樣做可以減少不必要的開銷描姚,優(yōu)化性能涩赢。*/
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
$emmit
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (process.env.NODE_ENV !== 'production') {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
let cbs = vm._events[event]
if (cbs) {
/*將類數(shù)組的對象轉(zhuǎn)換成數(shù)組*/
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
/*遍歷執(zhí)行*/
for (let i = 0, l = cbs.length; i < l; i++) {
cbs[i].apply(vm, args)
}
}
return vm
}
我們再來看看使用
Vue.$on("someFun",(data)=>{ ...dosomething})
Vue.$emmit("someFun",this.data)
在JS中怎么處理呢
//每一個on都會這樣
let callback[key].push(callbackFunction)
//在emmit的時候
callback[key].forEach(item=>item(data))
Vue通過設定對象屬性的 setter/getter 方法來監(jiān)聽數(shù)據(jù)的變化,通過getter進行依賴收集轩勘,而每個setter方法就是一個觀察者谒主,在數(shù)據(jù)變更的時候通知訂閱者更新視圖。
小結(jié): 發(fā)布訂閱模式就是 把觀察者放在一個Array中 然后在被觀察者發(fā)生改變時通知所有的觀察者赃阀,使用回調(diào)函數(shù)的方式通知霎肯。
中介者模式
通過設置 消息中心 (message center),避免組件之間直接依賴
所有的 協(xié)同者 (colleague) 只能通過 中介者 (mediator) 進行通信榛斯,
而相互之間不知道彼此的存在
當各個組件的消息出現(xiàn)循環(huán)時观游,消息中心可以消除組件之間的依賴混亂
var mediator = (function() {
var topics = {},
subUid = -1;
var publish = function(topic, args) {
if (!topics[topic]) {
return false;
}
var subscribers = topics[topic],
len = subscribers ? subscribers.length : 0;
while (len--) {
subscribers[len].func(topic, args);
}
return true;
};
var subscribe = function(topic, func) {
if (!topics[topic]) {
topics[topic] = [];
}
var token = (++subUid).toString();
topics[topic].push({
token: token,
func: func
});
return token;
};
return {
publish: publish,
subscribe: subscribe,
installTo: function(obj) {
obj.publish = publish;
obj.subscribe = subscribe;
}
}
}());
// 具體應用
var mod1 = {
run: function(arg) {
console.log('mod1 received ' + arg);
}
};
var mod2 = {};
var topic = 'myTopic';
mediator.installTo(mod1);
mediator.installTo(mod2);
// mod1訂閱消息
mod1.subscribe(topic, function(t, arg) {
mod1.run(arg);
});
// mod2發(fā)布消息
mod2.publish(topic, 'data');
總結(jié)
觀察者模式和中介者模式看起來非常的相似,核心原理都是一樣的驮俗,這里有一個細微的差別懂缕,中介者模式注重狀態(tài)告知,觀察者模式側(cè)重組件數(shù)據(jù)通信王凑,其實我們這里完全使用觀察者模式也可以實現(xiàn)狀態(tài)告知搪柑,不過 但觀察者是分發(fā)性的,所有的觀察者都會受到信息索烹,而且中介者則是單一的工碾,對象的通信由中介者處理。
中介者模式 一般通過 觀察者模式 實現(xiàn)
協(xié)同者 作為 發(fā)布者百姓,中介者 作為 觀察者
協(xié)同者 發(fā)布消息 -> 中介者 收到并處理消息 -> 中介者 直接發(fā)送消息給 協(xié)同者
協(xié)同者 不依賴于 中介者
當組件之間依賴關(guān)系簡單時渊额,可以直接使用 觀察者模式
當組件之間依賴關(guān)系復雜是,需要借助 中介者模式 梳理關(guān)系