在介紹基本概念之前,我想先梳理一下dva框架里面的數(shù)據(jù)流向,這個(gè)十分重要绽昼。多話不說,先上圖:
數(shù)據(jù)的改變發(fā)生通常是通過用戶交互行為或者瀏覽器行為(如路由跳轉(zhuǎn)等)觸發(fā)的须蜗,當(dāng)此類行為會(huì)改變數(shù)據(jù)的時(shí)候可以通過dispatch發(fā)起一個(gè) action硅确,如果是同步行為會(huì)直接通過Reducers改變State,如果是異步行為(副作用)會(huì)先觸發(fā)Effects然后流向Reducers最終改變State明肮,所以在 dva 中菱农,數(shù)據(jù)流向非常清晰簡明,并且思路基本跟開源社區(qū)保持一致(也是來自于開源社區(qū))柿估。
下面我們再來看看相關(guān)的重要概念循未,看概念的時(shí)候一定要聯(lián)系上面的圖片,腦中要清晰它的數(shù)據(jù)流向:
State
type State = any
State 表示 Model 的狀態(tài)數(shù)據(jù)秫舌,通常表現(xiàn)為一個(gè) javascript 對象(當(dāng)然它可以是任何值)的妖;操作的時(shí)候每次都要當(dāng)作不可變數(shù)據(jù)(immutable data)來對待,保證每次都是全新對象足陨,沒有引用關(guān)系羔味,這樣才能保證 State 的獨(dú)立性,便于測試和追蹤變化钠右。
在 dva 中你可以通過 dva 的實(shí)例屬性_store看到頂部的 state 數(shù)據(jù),但是通常你很少會(huì)用到:
constapp=dva();console.log(app._store);//頂部的 state 數(shù)據(jù)
reducers
以 key/value 格式定義 reducer忘蟹。用于處理同步操作飒房,唯一可以修改state的地方。由action觸發(fā)媚值。
格式為(state, action) => newState或[(state, action) => newState, enhancer]狠毯。
詳見:https://github.com/dvajs/dva/blob/master/test/reducers-test.js
Action
type AsyncAction = any
Action 是一個(gè)普通 javascript 對象,它是改變 State 的唯一途徑褥芒。無論是從 UI 事件嚼松、網(wǎng)絡(luò)回調(diào)嫡良,還是 WebSocket 等數(shù)據(jù)源所獲得的數(shù)據(jù),最終都會(huì)通過 dispatch 函數(shù)調(diào)用一個(gè) action献酗,從而改變對應(yīng)的數(shù)據(jù)寝受。action 必須帶有type屬性指明具體的行為,其它字段可以自定義罕偎,如果要發(fā)起一個(gè) action 需要使用dispatch函數(shù)很澄;需要注意的是dispatch是在組件 connect Models以后,通過 props 傳入的颜及。
dispatch({
????????type: 'add',
});
dispatch 函數(shù)
type dispatch = (a: Action) => Action
dispatching function 是一個(gè)用于觸發(fā) action 的函數(shù)甩苛,action 是改變 State 的唯一途徑,但是它只描述了一個(gè)行為俏站,而 dipatch 可以看作是觸發(fā)這個(gè)行為的方式讯蒲,而 Reducer 則是描述如何改變數(shù)據(jù)的。
在 dva 中肄扎,connect Model 的組件通過 props 可以訪問到 dispatch墨林,可以調(diào)用 Model 中的 Reducer 或者 Effects,常見的形式如:
dispatch({? type:'user/add',//如果在 model 外調(diào)用反浓,需要添加 namespacepayload:{},//需要傳遞的信息});
Reducer
type Reducer = (state: S, action: A) => S
Reducer(也稱為 reducing function)函數(shù)接受兩個(gè)參數(shù):之前已經(jīng)累積運(yùn)算的結(jié)果和當(dāng)前要被累積的值萌丈,返回的是一個(gè)新的累積結(jié)果。該函數(shù)把一個(gè)集合歸并成一個(gè)單值雷则。
Reducer 的概念來自于是函數(shù)式編程辆雾,很多語言中都有 reduce API。如在 javascript 中:
[{x:1},{y:2},{z:3}].reduce(function(prev,next){returnObject.assign(prev, next);})//return {x:1, y:2, z:3}
在 dva 中月劈,reducers 聚合積累的結(jié)果是當(dāng)前 model 的 state 對象度迂。通過 actions 中傳入的值,與當(dāng)前 reducers 中的值進(jìn)行運(yùn)算獲得新的值(也就是新的 state)猜揪。需要注意的是 Reducer 必須是純函數(shù)惭墓,所以同樣的輸入必然得到同樣的輸出,它們不應(yīng)該產(chǎn)生任何副作用而姐。并且腊凶,每一次的計(jì)算都應(yīng)該使用immutable data,這種特性簡單理解就是每次操作都是返回一個(gè)全新的數(shù)據(jù)(獨(dú)立拴念,純凈)钧萍,所以熱重載和時(shí)間旅行這些功能才能夠使用。
Effect
Effect 被稱為副作用政鼠,在我們的應(yīng)用中风瘦,最常見的就是異步操作。它來自于函數(shù)編程的概念公般,之所以叫副作用是因?yàn)樗沟梦覀兊暮瘮?shù)變得不純万搔,同樣的輸入不一定獲得同樣的輸出胡桨。
dva 為了控制副作用的操作,底層引入了redux-sagas做異步流程控制瞬雹,由于采用了generator的相關(guān)概念 昧谊,所以將異步轉(zhuǎn)成同步寫法,從而將effects轉(zhuǎn)為純函數(shù)挖炬。至于為什么我們這么糾結(jié)于純函數(shù)揽浙,如果你想了解更多可以閱讀Mostly adequate guide to FP,或者它的中文譯本JS函數(shù)式編程指南意敛。
Subscription
Subscriptions 是一種從源獲取數(shù)據(jù)的方法馅巷,它來自于 elm。
Subscription 語義是訂閱草姻,用于訂閱一個(gè)數(shù)據(jù)源钓猬,然后根據(jù)條件 dispatch 需要的 action。數(shù)據(jù)源可以是當(dāng)前的時(shí)間撩独、服務(wù)器的 websocket 連接敞曹、keyboard 輸入、geolocation 變化综膀、history 路由變化等等澳迫。
importkeyfrom'keymaster';...app.model({? namespace:'count',? subscriptions:{keyEvent(dispatch) {key('?+up, ctrl+up', ()=>{dispatch({type:'add'}) });? ? },? }});
Router
這里的路由通常指的是前端路由,由于我們的應(yīng)用現(xiàn)在通常是單頁應(yīng)用剧劝,所以需要前端代碼來控制路由邏輯橄登,通過瀏覽器提供的History API可以監(jiān)聽瀏覽器url的變化,從而控制路由相關(guān)操作讥此。
dva 實(shí)例提供了 router 方法來控制路由拢锹,使用的是react-router。
import{Router,Route}from'dva/router';app.router(({history})=>);
在組件設(shè)計(jì)方法中萄喳,我們提到過 Container Components卒稳,在 dva 中我們通常將其約束為 Route Components,因?yàn)樵?dva 中我們通常以頁面維度來設(shè)計(jì) Container Components他巨。
所以在 dva 中充坑,通常需要 connect Model的組件都是 Route Components,組織在/routes/目錄下染突,而/components/目錄下則是純組件(Presentational Components)匪傍。