? ? ? ?新入職的公司使用dva框架材义,之前沒有接觸過颖对,所以一邊學(xué)習(xí)赏寇,一邊工作吉嫩。記一下DVA框架的幾個基本概念。
首先放一下DVA框架數(shù)據(jù)流向:
? ? ? ?數(shù)據(jù)的改變發(fā)生通常是通過用戶交互行為或者瀏覽器行為(如路由跳轉(zhuǎn)等)觸發(fā)的嗅定,當(dāng)此類行為會改變數(shù)據(jù)的時候可以通過dispatch發(fā)起一個action自娩,如果是同步的行為,會直接通過reducer改變state渠退,如果是異步的行為(副作用)會先觸發(fā)effects然后流向reducer忙迁,最終改變state(一般是從服務(wù)器獲取數(shù)據(jù),拿到數(shù)據(jù)后觸發(fā)effects)碎乃,然后通過connect(一個函數(shù)姊扔,綁定state到view)渲染組件。所以在DVA中梅誓,數(shù)據(jù)流非常清晰簡明恰梢,并且思路基本跟開源社區(qū)保持一致。
DVA的幾個概念
Models組成:namespace梗掰,state嵌言,reducer,effect及穗, subscription
{
namespace: 'count',
state: 0,
reducers: {
add(state) { return state + 1 },
},
effects: {
*addAfter1Second(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'add' });
},
}
},
subscription{
}
State
? ? ? ?表示該model當(dāng)前的狀態(tài)摧茴。數(shù)據(jù)保存在這里,直接決定了視圖層的輸出拥坛。
? ? ? ?State表示Model的狀態(tài)數(shù)據(jù)蓬蝶,通常表現(xiàn)為一個js對象(當(dāng)然可以是任何值)尘分;操作的時候每次都要當(dāng)做不可變數(shù)據(jù)(immutable data)來對待猜惋,保證每次都是全新對象,沒有引用關(guān)系培愁,這樣才能保證State的獨立性著摔,便于測試和追蹤變化。
state:{
namespace:'businessApprove',
nodeList:[],
checkList:[]
}
? ? ? ?在DVA中定续,你可以通過DVA的實例屬性_store來看到頂部的state數(shù)據(jù)谍咆,但是通常很少用到:
const app = dva();
console.log(app._store);//頂部的state數(shù)據(jù)
Action
? ? ? ?一個對象禾锤,描述事件,可以是同步摹察,也可以是異步恩掷。
Action是一個普通js對象,他是改變State的唯一途徑供嚎。無論是從UI事件黄娘,網(wǎng)絡(luò)回調(diào),還是WebSocket等數(shù)據(jù)源說獲得的數(shù)據(jù)克滴,最終都會通過dispatch函數(shù)調(diào)用一個action逼争,從而改變對應(yīng)的數(shù)據(jù)。action必須帶有type屬性指明具體的行為劝赔,其他字段可以自定義誓焦。如果要發(fā)起一個action需要使用dispatch函數(shù),需要注意的是dispatch是在組件connect Models以后通過props傳入的着帽,通過connect將model中的元素作為props的方式傳遞給component杂伟。
dispatch({
type:'changeData',
payload:{
list:[],
}
})
export default connect()(BusinessApprove)
connect
????通過connect將module中的元素作為props的方式傳遞給component。
export default connect(({ businessApprove, loading }) => ({ businessApprove, loading }))(BusinessApprove)
dispatch函數(shù)
????dispatch函數(shù)是一個用于觸發(fā)action的函數(shù)仍翰,action是改變state的唯一途徑稿壁,但是他只是描述了一個行為,而dispatch可以看做是觸發(fā)這個行為的方式歉备,而reducer則是描述如何改變數(shù)據(jù)的傅是。
????在DVA中,connect Model的組件通過props可以訪問到dispatch蕾羊,可以調(diào)用Model中的reducer或者effects喧笔,常見的形式如:
dispatch({
type:`${namespace}/changeData`,
//如果在model外調(diào)用,需要添加namespace龟再,(`${namespace}`)
payload: {//需要傳遞的信息
list: [],
}
})
reducer
type Reducer<S,A> = (state:S,action:A) =>S
????reducer更新數(shù)據(jù)书闸,Action處理器,處理同步動作利凑,用來算出最新的state浆劲。
????reducer函數(shù)接受兩個參數(shù):之前已經(jīng)累積運算的結(jié)果和當(dāng)前要被累積的值,返回的是一個新的累積結(jié)果哀澈。該函數(shù)把一個集合歸并成一個單值牌借。reducer的概念來自于函數(shù)式編程。在DVA中割按,reducer聚合累積的結(jié)果是當(dāng)前model的state對象膨报。通過action中傳入的值,與當(dāng)前reducer中的值進行運算,獲得新的值(也就是新的state)现柠。需要注意的是院领,reducer必須是純函數(shù),所以同樣的輸入必然得到同樣的輸出够吩,它們不應(yīng)該產(chǎn)生任何副作用比然。并且,每一次的計算都應(yīng)該使用immutable data周循,這種特性簡單理解就是每次操作都是返回一個全新的數(shù)據(jù)(獨立谈秫、純凈),所以重?zé)彷d和時間旅行這些功能才能使用鱼鼓。
reducers: {
changeData (state, { payload }) {
return { ...state, ...payload }
},
...
}
effect
????請求數(shù)據(jù)拟烫,action處理器,處理異步動作迄本。底層引入了redux-saga做異步流程控制硕淑,采用了generator的相關(guān)概念。effect是一個generator函數(shù)嘉赎,內(nèi)部使用yeild關(guān)鍵字置媳,標(biāo)識每一步的操作(不管是異步或者同步)。
????effect被稱為副作用公条。在我們的應(yīng)用中拇囊,最常見的就是異步操作。它來自于函數(shù)編程的概念靶橱,之所以叫副作用是因為它使我們的函數(shù)變得不純寥袭,同樣的輸入不一定獲得同樣的輸出。DVA為了控制副作用的操作关霸,底層引入了redux-saga做異步流程控制传黄,由于采用了generator的相關(guān)概念,所以將異步轉(zhuǎn)成同步寫法队寇,從而將effect轉(zhuǎn)換為純函數(shù)膘掰。
effects: {
// 查詢付款列表
* query ({ payload = {} }, { call, put, select }) {
const data = yield call(query, payload)//發(fā)起ajax請求 service內(nèi)的query異步函數(shù)
// const { tableKey } = yield select(({ paymentApprove }) => paymentApprove)//獲取model中的state
if (data.success) {
yield put({//提交reducer更改state
type: 'changeData',
payload: {
list: data.data.list,
},
})
} else {
message.error(data.message, 1)
}
},
call和put
????DVA提供多個effect函數(shù)內(nèi)部的處理函數(shù),比較常用的是call和put佳遣。
????call:執(zhí)行異步函數(shù)
????put:發(fā)出一個action识埋,調(diào)用給定的函數(shù),類似于dispatch
????select:從全局中取數(shù)據(jù)
const num = yield select(
state => state.count.num
);
subscription
????用于訂閱一個數(shù)據(jù)源零渐,然后根據(jù)條件dispatch需要的action窒舟。數(shù)據(jù)源可以是當(dāng)前的時間,服務(wù)器的websocket連接相恃,keyboard的輸入辜纲,geolocation變化,history路由變化等等拦耐。subscription中無法監(jiān)聽state中的數(shù)據(jù)變化耕腾。
????model分兩類,一是全局model杀糯,二是頁面model扫俺。全局model存在于/src/model/目錄,所有頁面都可以引用固翰,頁面model不能被其他頁面所引用狼纬。
????規(guī)則如下:
- src/models/*/.js 為 global model
- src/pages//models//*.js 為 page model
- global model 全量載入,page model 在 production 時按需載入骂际,在 development 時全量載入
- page model 為 page js 所在路徑下 models/*/.js 的文件
- page model 會向上查找疗琉,比如 page js 為 pages/a/b.js,他的 page model 為 pages/a/b/models//.js + pages/a/models//.js歉铝,依次類推
- 約定 model.js 為單文件 model盈简,解決只有一個 model 時不需要建 models 目錄的問題,有 model.js 則不去找 models/*/.js
dva01-02.png
如上目錄:
● global model 為 src/models/g.js
● /a 的 page model 為 src/pages/a/models/{a,b,ss/s}.js
● /c 的 page model 為 src/pages/c/model.js
● /c/d 的 page model 為 src/pages/c/model.js, src/pages/c/d/models/d.js