简介
官网上介绍:dva 是基于redux,redux-saga和react-router的轻量级前端框架。也就是 dva 主要是基于 redux 和 redux-saga 的数据流解决方案,内置了 react-router 和 fetch。
从何处开始接触
dva 是 antd design pro 基于 React 的中后台管理控制台的脚手架选择的数据流管理框架,做 antd-pro 搭建的项目的时候开始接触到这个框架,在脚手架形成的目录中:dva 分别在 '/src/models' 中管理全局数据,在 '/pages/确定的某一页面/models' 中管理某一页面的数据。
结合代码理解
下面代码是在 antd-pro 脚手架搭建起来的项目中的实践,原理部分的掌握还欠缺,先记下来常用的用法,和基础知识。
- 这可能是在 page 中的一段简单代码,组件要改变当前状态需要调用 dispatch 函数驱动 dva model state 的改变,改变后的 state 再通过 connect 注入到组件中:
// connect 也是连接的数据是对 store 中的数据的浅复制,当是引用类型的数据的时候,如果直接修改 props 中映射数据, store 里面的数据也会被修改
@connect(state => ({
detailsTable: state['event/addNewCard'],// 这里传入的 state 参数是一个“完全体”,包含了所有 namespace 下的 state,以 namespace 为 key 值进行区分
loading: state.loading,
layout: state.layout,
}))
export default class PuzzleCardsPage extends Component {
addNewCard(){
dispatch({
type:'event/addNewCard',
payload:{},//传递数据的载体
//还可以传进去一个回调函数,处理当数据没有正确请求的时候的处理逻辑
callback: res => {
message.error(res)
}
})
}
render() {
return (
<div>
<Button onClick={this.addNewCard}> 添加卡片 </Button>
</div>
);
}
}
- model 是 dva 中最重要的一个概念,一个简单的 model 示例如下:
export default {
namespace: '',//不同的页面或操作对应不同的命名空间
state:{},
effects:{//相当于 redux 的 middleware,为了保证 reducer 的纯粹性,有一些需要对数据在进行处理的操作放在 effects 里面,比如在 effects 里面可以调用 services 方法进行异步请求。
*addByONe({payload, error}, { call, put, select }) { // 传入这个 Generator 函数的有两个参数,一个是匹配这个 effects 的 action 对象,因此可以取到 payload 和 error 字段。 另一个是 effect 原语集,call 主要处理异步逻辑(比如发起请求)它第一个参数是一个函数,要求函数返回 Promise,之后的参数是该函数调用时的入参。
const num = yield select(state => state.num) // effects 里面主要通过yield select 获取 state
let param1;
param1 = num + param;
yield put({//effects 里面通过 put 方法触发 action,和 pages 中的dispatch 方法一模一样,可以继续请求其他 namespace 中的 effects 或者 reducer ,不同的是,请求自身的 effects 或者 reducer 时不需要指定 namespace 名称
type: 'save',
num:param1
});
}
},
//用来保存更新 state 值 上面的 put 方法调用这里的方法,当不需要发起请求,只修改state中数据的方法放在这里,且这里的方法也能够被外部调用
reducers: {
save(state, action) { //state 是当前命名空间的 state,这里的 action 包含了上面传递的参数和 type
return { ...state, ...action.num };
},
},
}
model 中几个基本属性,其中 namespace 和 state 是一个 model 中最基本的两个成员。
namespace
:model 的命名空间,只能用字符串,是一个 model 的唯一标识。一个大型应用可能包含多个 model,通过 namespace 区分。state
:当前 model 状态的初始值,表示当前状态。是该 model 管理的数据reducers
:用于处理同步操作,更新修改 state,由 action 触发。reducer 是一个纯函数,它接受当前的 state 及一个 action 对象。action 对象里面可以包含数据体(payload)作为入参,需要返回一个新的 state。effects
:用于处理异步操作(例如:与服务端交互)和复杂的数据处理逻辑,也是由 action 触发。但是,它不可以修改 state,要通过触发 action 调用 reducer 实现对 state 的间接操作。action
:是 reducers 及 effects 的触发器,一般是一个对象,形如{ type: 'add', payload: todo },通过 type 属性可以匹配到具体某个 reducer 或者 effect,payload 属性则是数据体,用于传送给 reducer 或 effect。
- 如果想知道数据请求函数是否执行完毕,需要加载
dva-loading
插件,但是使用脚手架搭建项目,直接在页面中使用就可以了,通过 connect 方法将加载的状态传递到组件中,下面是几种具体的方法:
(1)loading.effects['chart/fetch']
监听这个方法是否执行完毕,若执行完毕。loading 的值就变为 false。
@connect(({ chart, loading }) => ({
chart,
loading: loading.effects['chart/fetch'],
}))
(2)loading.models.monitor
监听这个模块是否执行完毕,注意这里的 [monitor] 是命名空间。监听的是当前页面用到的命名空间里面的方法。若执行完毕,loading 的状态变为 false。
@connect(({ monitor, loading }) => ({
monitor,
loading: loading.models.monitor,
}))
(3)还有一种用法是 loading.global,通过全局的状态控制 loading 的状态。loading.global
监听这个页面的加载状态。当页面处于异步加载状态时该值为 true,当页面加载完成时,自动监听该值为 false。
这里是绑定到 dva 的自动控制 loading 数据的插件。不再需要重复写 showLoading 和 hideLoading。
当不使用 antd-pro 脚手架搭建项目使用 dva 时
文档中讲的很清楚,而且在掌握上面的基础知识后,也不难上手使用这个框架。
// 创建应用
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 />);
// 启动应用,将组件挂载到 id 为 root 的元素上
app.start('#root');