Dva是什么
dva 是基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装,没有引入任何新概念,全部代码不到 100 行。( Inspired by elm and choo. )
- 框架,而非类库
- 基于redux, react-router, redux-soga的轻量级封装
- 借鉴elm的概念, Reducer, Effect和Subscription
- …
可以说,dva是基于react+redux最佳实践上实现的封装方案,简化了redux和redux-saga使用上的诸多繁琐操作。
文件结构
官方推荐的:
├── /mock/ # 数据mock的接口文件 ├── /src/ # 项目源码目录 │ ├── /components/ # 项目组件 │ ├── /routes/ # 路由组件(页面维度) │ ├── /models/ # 数据模型 │ ├── /services/ # 数据接口 │ ├── /utils/ # 工具函数 │ ├── route.js # 路由配置 │ ├── index.js # 入口文件 │ ├── index.less │ └── index.html ├── package.json # 定义依赖的pkg文件 └── proxy.config.js # 数据mock配置文件
初体验
一个小Demo
5个API
app = dva(Opts)
app.use(Hooks)
app.models(ModelObject)
app.router(Function)
app.start([HTMLElement])
8个概念
- State
- Action
- Model
- Reducer
- Effect
- Subscription
- Router
- RouteComponent
数据流向

数据的改变发生通常是通过:
- 用户交互行为(用户点击按钮等)
- 浏览器行为(如路由跳转等)触发的
当此类行为会改变数据的时候可以通过 dispatch 发起一个 action,如果是同步行为会直接通过 Reducers 改变 State ,如果是异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变 State 。
所以在 dva 中,数据流向非常清晰简明,并且思路基本跟开源社区保持一致。如上图。
相关概念与理解
定义model
namespace: 'monthCard', state: { list: [], total: 0, editData: {}, loading: false }, subscriptions: { setup({ dispatch, history}){ } }, effects: {}, reducers: {}
State是整个应用的数据层。应用的state被存储在一个object tree中。应用的初始state在model中定义,也就是说,由model state组成全局state。操作的时候每次都要当作不可变数据(immutable data)来对待,保证每次都是全新对象,没有引用关系,这样才能保证 State 的独立性,便于测试和追踪变化。
namespace
是model state在全局state中所用到的key。
这里的Model非MVC中的M,是用于把数据相关的逻辑聚合到一起。
完成component
const monthCardPrice = ({ monthCard, monthCard: { list }, diapacth }) => { const queryList = () => { dispatch({ type: `monthCard/query`, payload: {}//需要传递的数据 }) } return ( <div> <button onclick={queryList}>查询</button> </div> ) } const mapStateToProps = ({ monthCard, global }) => { return { monthCard, global } } export default connect(mapStateToProps)(monthCardPrice)
RouteComponent 表示 Router 里匹配路径的 Component,通常会绑定 model 的数据
Presentational Component是独立的纯粹的,例如ant.design UI组件的react实现,每个组件跟业务数据并没有耦合关系,只是完成自己独立的任务,需要的数据通过 props 传递进来,需要操作的行为通过接口暴露出去。 而 Container Component 更像是状态管理器,它表现为一个容器,订阅子组件需要的数据,组织子组件的交互逻辑和展示。
所以在 dva 中,通常需要 connect Model的组件都是 Route Components,组织在/routes/目录下,而/components/目录下则是纯组件(Presentational Components)。
dva架构里component中基本不需要用到state。
- 绑定数据
这里利用es6结构赋值通过props将参数传入组件。monthCard
对应model上的state,通过connect
来绑定model state。这里connect来自react-redux。意味着Component里可以拿到Model中定义的数据,Model中也能接收到Component里dispatch的action。实现了Model和Component的连接。 - Action
Action表示操作事件,可以是同步,也可以是异步。Action 是一个普通 javascript 对象,它是改变 State 的唯一途径。无论是从 UI 事件、网络回调,还是 WebSocket 等数据源所获得的数据,最终都会通过 dispatch 函数调用一个 action,从而改变对应的数据。
dispatch 函数,通过 type 属性指定对应的 actions 类型,而这个类型名在 reducers(effects)会一一对应,从而知道该去调用哪一个 reducers(effects),除了 type 以外,其它对象中的参数随意定义,都可以在对应的 reducers(effects)中获取,从而实现消息传递,将最新的数据传递过去更新 model 的数据(state)
注意:Action在model自身模型以外定义时需要加model的namespace前缀, 在model中定义不需要加。
- 添加样式:
CSS Modules会给组件的className加上hash字符串,来保证className仅对引用了样式的组件有效,如styles.normal可能会输出为normal___39QwY。
className的输出格式可以通过webpack.config进行修改。
更新state
namespace: 'monthCard', state: { list: [], total: 0, editData: {}, loading: false }, subscriptions: {}, effects: {}, reducers: { + updateState(state, action){ + return { + ...state, + ...action.payload + } + } }
reducer 是唯一可以更新 state 的地方,这个唯一性让我们的 App 更具可预测性,所有的数据修改都有据可查。reducer 是 pure function,他接收参数 state 和 action,返回新的 state,通过语句表达即 (state, action) => newState。该函数把一个集合归并成一个单值。
Reducer 的概念来自于是函数式编程,在 dva 中,reducers 聚合积累的结果是当前 model 的 state 对象。通过 actions 中传入的值,与当前 reducers 中的值进行运算获得新的值(也就是新的 state)。
注意:Reducer函数必须是纯函数。
Effects异步处理
+ //异步请求 //request 是我们封装的一个网络请求库 + async function queryFromService(data) { + return request("queryFromApi", { + data, + method: "post", + dataType: "payload" + }) + } namespace: 'monthCard', state: { list: [], total: 0, editData: {}, loading: false }, subscriptions: {}, effects: { + * query({ payload }, { call, put }){ + yield put ({ + type: `updateState`, + payload: { + loading: true + } + }) + + const { data } = yield call (queryFromService, parse(payload)) + + if(data){ + yield put({ + type: 'querySuccess', + payload: { + loading: false, + list: data.data, + editData: data.data, + total: data.recordsTotal + } + }) + } else { + yield put({ + type: 'querySuccess', + payload: { + data: {}, + loading: false + } + }) + } + }, + * create(){}, + * 'delete'(){},//delete是关键字 + * update(){} }, reducers: { updateState(state, action){ return { ...state, ...action.payload } }, + querySuccess(state, action){ + if(action.payload.data){ + const { + data: { + status, + message + } + } = action.payload + + if(1 == status){ + // + }else { + Message.error(message) + } + } + + return { + ...state + } + } }
当数据需要从服务器获取时,需要发起异步请求,请求到数据之后,通过调用 Reducers更新数据到全局state。dva 通过对 model 增加 effects 属性来处理 side effect(异步任务),这是基于 redux-saga 实现的,语法为 generator。Generator 返回的是迭代器,通过 yield 关键字实现暂停功能。Redux-saga 中文文档。
* query(action, {call, put, select}){}
表示一个worker Saga,监听所有的query
action,并触发一个Api调用以获取服务器数据。当每个query
action被发起时调用 call 和 put 都是 redux-saga 的 effects,call 表示调用异步函数,put 表示 dispatch action,其他的还有 select, take, fork, cancel 等,select 则可以用来访问其它 model。格式:*(action, effects) => void
在Effects里,Generator函数通过yield命令将异步操作同步化,无论是yield 亦或是 async 目的只有一个: 让异步编写跟同步一样 ,从而能够很好的控制执行流程。
订阅数据源
subscriptions: { setup(( dispatch, history )){ history.listen(({ pathname, query }) => { if(pathToRegexp(`/y/monthCard/list`).test(pathname)) { dispatch({ type:`query` }) } }) } }
Subscriptions 表示订阅,用于订阅一个数据源,然后按需 dispatch action。格式为 ({ dispatch, history }) => unsubscribe 。比如:当用户进入 /y/monthCard/list 页面时,触发 action query 加载数据。
如果 url 规则比较复杂,比如 /users/:userId/search,那么匹配和 userId 的获取都会比较麻烦。这是推荐用 path-to-regexp 简化这部分逻辑。
定义路由
路由决定进入url渲染哪些Component。history 默认是 hashHistory 并且带有 _k
参数,可以换成 browserHistory,也可以通过配置去掉 _k
参数。
工具
dva使用总结
dva将所有与数据操作相关的逻辑集中放在一个地方处理和维护,在数据跟业务状态交互比较紧密的场景下,会使我们的代码更加清晰可控。尤其适用于数据跟业务状态关联性极强的企业级后台信息管理系统。
对于一个企业级后台管理系统,由于要进行大量的数据操作,在设计model时将不同类型的业务需求数据操作分开处理,便于维护。
项目的开发流程一般是从设计model state开始进行抽象数据,完成component后,将组件和model建立关联,通过dispatch一个action,在reducer中更新数据完成数据同步处理;当需要从服务器获取数据时,通过Effects数据异步处理,然后调用Reducer更新全局state。是一个单向的数据流动过程。解决了redux中代码分散和重写问题,总之,Dva:Build redux application easier and better。
学习资料
官方地址
Redux-saga 中文文档
dva-knowledgemap
dva: react application arch in ant financial
作者:黎贝卡beka
链接:https://www.jianshu.com/p/f7401adce447
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。