Dva 是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。
Dva 的本意,是将基于 React 技术栈中常用到的库集成到一起。当时,React 社区中最流行的应用架构方案如下:
- 路由: React-Router
- 状态管理: Redux
- 异步操作: Redux-saga
而 Dva 则是将上面三个 React 工具库包装在一起,简化了 API,让开发 React 应用更加方便和快捷。因此:
Dva = React-Router + Redux + Redux-saga
特性
- 易学易用,仅有 6 个 api,对 redux 用户尤其友好,配合 umi 使用后更是降低为 0 API
- elm 概念,通过 reducers, effects 和 subscriptions 组织 model
- 插件机制,比如 dva-loading 可以自动处理 loading 状态,不用一遍遍地写 showLoading 和 hideLoading
- 支持 HMR,基于 babel-plugin-dva-hmr 实现 components、routes 和 models 的 HMR
数据流向
数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据的时候可以通过 dispatch 发起一个 action,如果是同步行为会直接通过 Reducers 改变 State
如果是异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变 State。Dva 中的核心概念:
- app.model
- namespace: 当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成
- state: 该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出
- reducers: Action 处理器,处理同步动作,用来算出最新的 State
- effects:Action 处理器,处理异步动作,Effect 是一个 Generator 函数,内部使用 yield 关键字,标识每一步的操作(不管是异步或同步)
- dva 提供多个 effect 函数内部的处理函数,比较常用的是
call
和put
- call:执行异步函数
- put:发出一个 Action,类似于 dispatch
- dva 提供多个 effect 函数内部的处理函数,比较常用的是
简单示例
- 定义 Model
import {getStuListApi} from "../services/stuApi";
export default {
namespace: "stuModel",
// 仓库数据
state: {
stuList: [],
},
// 副作用
effects: {
// 从服务器获取学生数据
// call 用来发送请求,put 用于同步更新本地仓库数据
*_getStuList(_, { call, put }) {
// 从服务器拿到数据
const {data} = yield call(getStuListApi);
// 将数据同步到本地的状态仓库
yield put({
type : "initStuList",
data
})
},
},
// reducer,同步更新本地的仓库
reducers: {
initStuList(state, action){
let obj = {...state};
obj.stuList = [...action.data];
return obj;
}
},
};
- connect 链接
import React from 'react';
import { connect } from 'dva';
import StudentList from '../components/StudentList';
const Student = ({ dispatch, stuList }) => {
function handleDelete(id) {
dispatch({
type: 'products/delete',
payload: id,
});
}
return (
<div>
<h2>List of Products</h2>
<StudentList onDelete={handleDelete} products={products} />
</div>
);
};
// export default Products;
export default connect(({ stuModel }) => ({
stuList: stuModel.stuList,
}))(Student);