該篇文章適合的閲讀人羣:剛接手公司項目,技術棧為react,狀態管理直接用的dva,但是不太瞭解redux、redux-saga、react-redux的人員,想要快速上手業務開發,看這篇就對了!
1. Dva是什麼?
dva 首先是一個基於 redux 和 redux-saga 的數據流方案,然後為了簡化開發體驗,dva 還額外內置了 react-router 和 fetch,所以也可以理解為一個輕量級的應用框架。
2. 快速上手開發?(保存用户信息為例)
- 定義Model文件用於全局的用户信息數據管理,如下代碼:
// 引入接口方法用於在effects中編寫所需的業務邏輯
import { queryCurrent } from '@/services/user'
// 實際上只是按規則定義一個配置對象
const UserModel = {
// 當前 Model 的名稱。整個應用的 State,由多個小的 Model 的 State 以 namespace 為 key 合成
namespace: 'user',
// 該 Model 當前的狀態。數據保存在這裏,直接決定了視圖層的輸出
state: {
currentUser: {}
},
// Action 處理器,處理異步動作,基於 Redux-saga 實現。Effect 指的是副作用。根據函數式編程,計算以外的操作都屬於 Effect,典型的就是 I/O 操作、數據庫讀寫。
effects: {
// Effect 是一個 Generator 函數,內部使用 yield 關鍵字,標識每一步的操作(不管是異步或同步)。
*fetchCurrentUser({ payload, callback }, { call, put }) {
// 執行異步函數
const response = yield call(queryCurrent)
if(response.success) {
const { user } = response
if(callback) {
callback(user)
}
// 發出一個 Action,類似於 dispatch
yield put({
type: 'saveCurrentUser',
payload: user
})
}
}
},
// Reducer 是 Action 處理器,用來處理同步操作,可以看做是 state 的計算器。它的作用是根據 Action,從上一個 State 算出當前 State。
reducers: {
saveCurrentUser(state, { payload = {} }) {
return {
...state,
currentUser: payload
}
}
}
}
export default UserModel;
- 使用
connect連接上Dva並使用,如下代碼:
import { connect } from 'dva'
const SetUserInfoComponent = props => {
const { dispatch } = props
// 1. 調用dva附加在組件props上的dispatch方法,發起一個action
// 2. action是一個普通對象(plain object),一般包含type字段和payload字段(當然也可以視情況不傳payload)
dispatch({
// 3. type的值實際上類似一個尋址過程,編寫規則即namespace/reducerName|effectName
// 4. dva根據你的namespace去找到對應的model,根據你的reducerName|effectName找到對應的reducer或者effect
// 5. 回到我們這個例子即:找到namespace為user的model,並觸發一個名為fetchCurrentUser的effect,payload為空。
// 注意:實際上第5點描述的就是這個對象的鍵值對,這就是action,一個描述動作的普通對象。
type: 'user/fetchCurrentUser'
})
}
export default connect(({ user }) => ({ user }))(SetUserInfoComponent)
以下部分是詳細説明:connect是一個高階函數,它接收一個mapStateToProps函數(當然你也可以隨便叫它什麼名字,無非這個名字比較能闡明它的作用), 返回一個接收組件的函數。該組件增強函數是一個普通函數,接收組件,返回增強後的組件。使得增強後的組件可以在內部通過props拿到dva中的state、dva提供的dispatch函數。
// connect第一個形參函數,描述了需要從全局state映射到組件props上的指定部分狀態,該函數返回了一個對象內含狀態鍵值對
const mapStateToProps = ({ user }) => ({ user })
connect(mapStateToProps)(Component) => <EnhancedComponent {...props, user: GlobalState[user], dispatch: fn(){/* dispatch function code*/}}/>
//我們可以簡單想一下connect的實現形式可能類似如下代碼幫助理解
const connect = mapStateToPropsFunc => MetaComponent => {
const state = mapStateToPropsFunc(dva.GlobalState)
return <MetaComponent dispatch={dva.dispatch} {...state}/>
}
實際上翻閲Dva源碼可以知道connect方法就是由react-redux提供的(源碼看這裏),Dva做的工作就是引入導出connect便於我們統一在Dva中調用,即所謂的技術收斂。
3. 整體開發流程?
- 編寫你的業務組件
- 你意識到有些數據可能會跨組件使用
- 你為其定義了一個Model,聲明瞭一個獨一無二的namespace
- 你開始思考會涉及到哪些同步異步的狀態處理
- 初始化state,編寫effects、reducers(看上面例子的model部分!)
- 回到你的業務組件並將其連接到dva上(看上面例子的connect部分!)
- 在合適的地方dispatch一個action執行相應的reducer/effect(看上面例子的dispatch部分!)
4. 想要了解Dva源碼?
請先閲讀官網的這篇文章。
5. 更好的理解Dva?
- 你需要熟悉一下redux的架構
- 你需要理解react-redux的作用
- 思考一下redux負責同步狀態的管理,那麼該如何處理異步狀態?
- 看一下redux-thunk的源碼,並瞭解redux是怎麼用中間件思想讓狀態管理支持異步
- redux-saga解決了什麼,它是如何處理異步狀態的
- 整合構成了dva這一套狀態管理的最佳實踐,回過頭來看看文檔是不是很親切了。