場(chǎng)景是這樣的:
這樣一個(gè)操作, 需要同時(shí)依賴很多個(gè) Model, 因此這些程式碼不會(huì)寫(xiě)在某一個(gè) Model 裡邊.
有可能是例如 Backbone, 寫(xiě)在 ViewController 裡邊... 但是這樣程式碼復(fù)用不好, 而且 View 會(huì)變亂.
我目前採(cǎi)取的方案是用單獨(dú)一個(gè)文件去收集大部分的 Model 操作, 不過(guò)問(wèn)題是, 這個(gè)文件會(huì)不斷變大變亂.
所以這樣的問(wèn)題該如何解決?
延伸的問(wèn)題是, 怎麼整理這部分程式碼?
例如我用 React 的 Flux 方案, 盡量將流程理清楚, 就發(fā)現(xiàn)這部分程式碼不知道放在哪裡合適..
Flux 將使用者操作轉(zhuǎn)換為 Actions, Store 透過(guò) Dispatcher 監(jiān)聽(tīng)這些 Actions,
一個(gè) Actions 對(duì)應(yīng)多個(gè) Store 的時(shí)候... 問(wèn)題就來(lái)了:
我該用多個(gè) Actions 分別對(duì)應(yīng) Store, 還是一個(gè) Action 由多個(gè) Store 來(lái)監(jiān)聽(tīng)?
認(rèn)證0級(jí)講師
用戶點(diǎn)擊按鈕本質(zhì)上跟用戶透過(guò)URL訪問(wèn)一樣,都是一種‘輸入’,所以問(wèn)題和處理機(jī)制都是一樣的:在view層代碼裡監(jiān)聽(tīng)‘輸入’,處理一些view層(比如按鈕組件的toggle、URL的矯正)內(nèi)部的狀態(tài)變化,生成/提取出純粹的、抽象層級(jí)更高的(跟view組件或URL細(xì)節(jié)無(wú)關(guān))數(shù)據(jù)/訊息,用某種事件機(jī)制廣播出去,之後就跟自己沒(méi)關(guān)係了,接下來(lái)如果有controller層的話,在這部分的程式碼裡監(jiān)聽(tīng)這些事件,調(diào)用相應(yīng)model物件的方法(其中可能封裝了model物件自己之間的依賴關(guān)係和調(diào)用,但這裡的一對(duì)多複雜性不會(huì)暴露到外面去),同時(shí)也監(jiān)聽(tīng)某些model物件的狀態(tài)變化,呼叫對(duì)應(yīng)view物件的方法(或是重新渲染Virtual DOM)。所有東西綁定完成。
像我平常用NervJS(model) + DermJS(view)+ URLKit(route) 這樣的搭配,NervJS和DermJS物件都有自帶的事件方法,此外也可以在view/model物件初始化時(shí)傳入統(tǒng)一的bus事件對(duì)象。
你寫(xiě)一個(gè)UI component的時(shí)候當(dāng)然不會(huì)希望它依賴特定的model,寫(xiě)一個(gè)model組件的時(shí)候,也不會(huì)希望它依賴特定UI,所以一對(duì)多之類(lèi)的綁定是在另一個(gè)地方(專(zhuān)門(mén)的業(yè)務(wù)邏輯程式碼)完成的,view物件和model物件不需要也不應(yīng)該知道對(duì)方有幾個(gè)、是哪些,所以也不可能『多個(gè)Actions 分別對(duì)應(yīng)Store』。
至於『?jiǎn)为?dú)一個(gè)檔案』、『不斷變大變亂』的問(wèn)題,跟設(shè)定路由的檔案也是一回事,可以參考相關(guān)經(jīng)驗(yàn)。
flux裡,如果需要一個(gè)動(dòng)作對(duì)應(yīng)多個(gè)Store,其實(shí)也是很好解決的。
在Store裡面register回呼的時(shí)候,可以都對(duì)這個(gè)動(dòng)作進(jìn)行相應(yīng)就可以了,還可以透過(guò)waitFor
來(lái)改變對(duì)應(yīng)的順序。
如果擔(dān)心程式碼變亂的話,可以再單獨(dú)寫(xiě)一個(gè)constants
文件,定義好觸發(fā)的事件名稱(chēng)就可以了。
舉例:
點(diǎn)選一個(gè)按鈕,觸發(fā)send
事件,會(huì)更新兩個(gè)Store
分別是StoreA
和StoreB
??梢詫?xiě)一個(gè)constants.js
,先定義事件名稱(chēng):
constants:
module.exports = {
"ActionTypes": {
"SEND": "SEND"
}
};
然後在兩個(gè)Store
裡面分別註冊(cè)回調(diào):
StoreA:
var AppDispatcher = require('path/to/disp'),
constants = require('path/to/constants');
StoreA.dispatchToken = AppDispatcher.register(function(payload) {
var action = payload.action;
if (action.type === constants.ActionTypes.SEND) {
// callback A
};
});
StoreB:
var AppDispatcher = require('path/to/disp'),
constants = require('path/to/constants');
StoreB.dispatchToken = AppDispatcher.register(function(payload) {
var action = payload.action;
if (action.type === constants.ActionTypes.SEND) {
// callback B
};
});
在觸發(fā)點(diǎn)擊事件的時(shí)候,在Action
中觸發(fā)Disp
的這個(gè)事件,就會(huì)順序執(zhí)行在StoreA
和StoreB
中註冊(cè)的回調(diào)了:)
如果沒(méi)看過(guò),或是看過(guò)但忘了,這是一篇值得讀的文章:
Patterns For Large-Scale JavaScript Application Architecture
簡(jiǎn)單而言,你需要有些「約定俗成」的東西,讓view和model無(wú)需相互依賴(不管是1:1的依賴,還是1:N的依賴)。
用簡(jiǎn)單的event和observer pattern也可以,如果業(yè)務(wù)邏輯很複雜,用mediator完成模組間的通訊和同步。
PS:理想的情況是,你的每個(gè)模組都只知道自己(觸發(fā)什麼事件,聆聽(tīng)什麼事件),除此之外都不管,更不會(huì)知道對(duì)方的instance,或者mediator的instance。