摘要:以傳統(tǒng)方式來說,我們一般會(huì)搞一個(gè)store,上層業(yè)務(wù)的請求都是從它走,由它去拿到遠(yuǎn)程數(shù)據(jù),并且控制緩存。但我們面臨一個(gè)問題,當(dāng)數(shù)據(jù)更新了,需要通知所有使用這個(gè)數(shù)據(jù)的業(yè)務(wù)方,所以就不得不在這里再補(bǔ)一種機(jī)制,用于通知上層。通常就是某種訂閱機(jī)制:store1.subcribe("somedata", data => { //
以傳統(tǒng)方式來說,我們一般會(huì)搞一個(gè)store,上層業(yè)務(wù)的請求都是從它走,由它去拿到遠(yuǎn)程數(shù)據(jù),并且控制緩存。
但我們面臨一個(gè)問題,當(dāng)數(shù)據(jù)更新了,需要通知所有使用這個(gè)數(shù)據(jù)的業(yè)務(wù)方,所以就不得不在這里再補(bǔ)一種機(jī)制,用于通知上層。通常就是某種訂閱機(jī)制:
store1.subcribe("somedata", data => { //處理這個(gè)data,更新到視圖模型上供刷新界面 viewModel.somedata = data; });
這樣問題是解決了,但業(yè)務(wù)代碼就比較繁瑣了,為什么我還要在每個(gè)地方都寫訂閱?尤其是如果上層用了angular或者vue,本來可以很簡單的,你這一弄,框架剛幫我簡化了一些代碼,又被搞得很長了。
因?yàn)橹拔覀兊谝淮尾樵償?shù)據(jù),不是這么干的,而是:
service1.getData().then(data => { //處理這個(gè)data,更新到視圖模型上供刷新界面 viewModel.somedata = data; });
所以這里就出現(xiàn)了不同的兩種方式來更新界面數(shù)據(jù),這非常討厭。
我之前提出過一種折中的解決辦法,比如在angular中,可以這樣:
- store中保存數(shù)據(jù)對(duì)象的引用,上層業(yè)務(wù)在查詢的時(shí)候,持有這個(gè)引用,并且,不得修改內(nèi)容
- 如果需要修改或者更新遠(yuǎn)程數(shù)據(jù),調(diào)用store中封裝的修改方法,把數(shù)據(jù)合并到數(shù)據(jù)對(duì)象中
這樣,界面上的刷新之類,可以不勞自己費(fèi)心,但整體還是挺別扭的,而且,限制上層不能動(dòng)引用,這個(gè)事情很坑,萬一需要二次加工數(shù)據(jù),就歇菜了。
React有一套機(jī)制來處理這個(gè)事情,留給這派的人來闡述吧。
我說說另外一種方式,F(xiàn)RP。
在egghead上有一個(gè)RxJS的視頻系列,其中有一節(jié)大致介紹了使用Reactive理念處理這種場景,我覺得非常好,地址如下,可能要會(huì)員才能看:
Reactive Programming
它這個(gè)場景就是為了復(fù)用請求,里面的代碼摘錄:
var startupRequestStream = Rx.Observable.just('https://api.github.com/users');var requestOnRefreshStream = refreshClickStream .map(ev => { var randomOffset = Math.floor(Math.random()*500); return 'https://api.github.com/users?since=' + randomOffset; });var requestStream = startupRequestStream.merge(requestOnRefreshStream);var responseStream = requestStream .flatMap(requestUrl => Rx.Observable.fromPromise(jQuery.getJSON(requestUrl)) ) .shareReplay(1); // refreshClickStream: -------f-------------> // requestStream: r------r-------------> // responseStream: ---R-------R---------> // closeClickStream: ---------------x-----> // suggestion1Stream: N--u---N---u---u-----> function getRandomUser(listUsers) { return listUsers[Math.floor(Math.random()*listUsers.length)];}function createSuggestionStream(responseStream, closeClickStream) { return responseStream.map(getRandomUser) .startWith(null) .merge(refreshClickStream.map(ev => null)) .merge( closeClickStream.withLatestFrom(responseStream, (x, R) => getRandomUser(R)) );}
這里面的關(guān)鍵在于:shareReplay和withLatestFrom。
shareReplay的介紹在這:shareReplay | RxJS
withLatestFrom的介紹在這:withLatestFrom
拋開代碼細(xì)節(jié)不談,我覺得理念是這樣:
我把請求當(dāng)做流,然后,上層業(yè)務(wù)來訂閱這個(gè)流,正常來說,我們的期望可能是,訂閱之后,流的每次更新都會(huì)得到處理,但這時(shí)可能出現(xiàn):
某一個(gè)訂閱者來得太晚,流已經(jīng)走了一段數(shù)據(jù)了,會(huì)需要“補(bǔ)課”。
所以,Rx的機(jī)制會(huì)允許我們緩存一些數(shù)據(jù),供“我們來晚了”的朋友們使用。
使用這種流的理念來處理,有什么好處呢?那就是可以保證上層業(yè)務(wù)的一致性。對(duì)于上層代碼,我只管是從這個(gè)流獲取數(shù)據(jù),更新界面,我不關(guān)注你是哪來的,只認(rèn)你一家,反正只要根據(jù)你那邊的信息,都作了對(duì)應(yīng)處理,就肯定好了。
而這個(gè)流的提供方,也很方便,他只需考慮把不同來源的數(shù)據(jù)進(jìn)行歸并,從多個(gè)流合并成一個(gè)流,就是這么簡單。
Angular 2深度整合了RxJS,可以關(guān)注,不過RxJS本身是不綁定到任何框架的,你跟jQuery或者React配合使用,也是沒問題。