截止 2017.1,最流行的社區 React 應用架構方案如下。
- 路由: React-Router
- 架構: Redux
- 異步操作: Redux-saga
缺點:要引入多個庫,項目結構復雜。
dva 是什么
dva 是 React 應用框架,將上面三個 React 工具庫包裝在一起,簡化了 API,讓開發 React 應用更加方便和快捷。
dva = React-Router + Redux + Redux-saga
dva 應用的最簡結構
import dva from 'dva'; const App = () => <div>Hello dva</div>; // 創建應用 const app = dva(); // 注冊視圖 app.router(() => <App />); // 啟動應用 app.start('#root');
數據流圖
核心概念
- State:一個對象,保存整個應用狀態
- View:React 組件構成的視圖層
- Action:一個對象,描述事件
- connect 方法:一個函數,綁定 State 到 View
- dispatch 方法:一個函數,發送 Action 到 State
State 和 View
State 是儲存數據的地方,收到 Action 以后,會更新數據。
View 就是 React 組件構成的 UI 層,從 State 取數據后,渲染成 HTML 代碼。只要 State 有變化,View 就會自動更新。
Action
Action 是用來描述 UI 層事件的一個對象。
{ type: 'click-submit-button', payload: this.form.data }
connect 方法
connect 是一個函數,綁定 State 到 View。
import { connect } from 'dva'; function mapStateToProps(state) { return { todos: state.todos }; } connect(mapStateToProps)(App);
connect 方法返回的也是一個 React 組件,通常稱為容器組件。因為它是原始 UI 組件的容器,即在外面包了一層 State。
connect 方法傳入的第一個參數是 mapStateToProps 函數,mapStateToProps 函數會返回一個對象,用於建立 State 到 Props 的映射關系。
dispatch 方法
dispatch 是一個函數方法,用來將 Action 發送給 State。
dispatch({ type: 'click-submit-button', payload: this.form.data })
dispatch 方法從哪里來?被 connect 的 Component 會自動在 props 中擁有 dispatch 方法。
connect 的數據從哪里來?
dva 應用的最簡結構(帶 model)
// 創建應用 const app = dva(); // 注冊 Model app.model({ namespace: 'count', state: 0, reducers: { add(state) { return state + 1 }, }, effects: { *addAfter1Second(action, { call, put }) { yield call(delay, 1000); yield put({ type: 'add' }); }, }, }); // 注冊視圖 app.router(() => <ConnectedApp />); // 啟動應用 app.start('#root');
數據流圖 1
數據流圖 2
app.model
dva 提供 app.model 這個對象,所有的應用邏輯都定義在它上面。
const app = dva(); // 新增這一行 app.model({ /**/ }); app.router(() => <App />); app.start('#root');
Model 對象的例子
{ namespace: 'count', state: 0, reducers: { add(state) { return state + 1 }, }, effects: { *addAfter1Second(action, { call, put }) { yield call(delay, 1000); yield put({ type: 'add' }); }, }, }
Model 對象的屬性
- namespace: 當前 Model 的名稱。整個應用的 State,由多個小的 Model 的 State 以 namespace 為 key 合成
- state: 該 Model 當前的狀態。數據保存在這里,直接決定了視圖層的輸出
- reducers: Action 處理器,處理同步動作,用來算出最新的 State
- effects:Action 處理器,處理異步動作
Reducer
Reducer 是 Action 處理器,用來處理同步操作,可以看做是 state 的計算器。它的作用是根據 Action,從上一個 State 算出當前 State。
一些例子:
// count +1 function add(state) { return state + 1; } // 往 [] 里添加一個新 todo function addTodo(state, action) { return [...state, action.payload]; } // 往 { todos: [], loading: true } 里添加一個新 todo,並標記 loading 為 false function addTodo(state, action) { return { ...state, todos: state.todos.concat(action.payload), loading: false }; }
Effect
Action 處理器,處理異步動作,基於 Redux-saga 實現。Effect 指的是副作用。根據函數式編程,計算以外的操作都屬於 Effect,典型的就是 I/O 操作、數據庫讀寫。
function *addAfter1Second(action, { put, call }) { yield call(delay, 1000); yield put({ type: 'add' }); }
Generator 函數
Effect 是一個 Generator 函數,內部使用 yield 關鍵字,標識每一步的操作(不管是異步或同步)。
call 和 put
dva 提供多個 effect 函數內部的處理函數,比較常用的是 call
和 put
。
- call:執行異步函數
- put:發出一個 Action,類似於 dispatch
參考:dva 是什么