dva是一个轻量级的数据流解决方案,基于redux和redux-saga,还内置react-router和fetch。
dav简化了项目的数据流的构建方式,项目构建流程分为:快速构建项目->编写路由文件->注册路由->编写UI组件文件->定义Model->载入Model->模型和组件connect起来。
一、项目构建
1.快速构建项目
使用工具快速构建项目模板。
1 2 3 4 5 6 7
| $ npm install dva-cli -g
$ dva new dva-quickstart
$ cd dva-quickstart
$ npm start
|
项目即在本地8000端口启动。
2.编写路由文件
先安装ant和按需加载antd的扩展babel-plugin-import
$ npm install antd babel-plugin-import --save
新建路由组件routes/Products.js
1 2 3 4 5 6 7
| import React from 'react';
const Products = (props) => ( <h2>List of products</h2> );
exprot default Products;
|
3.注册路由
编辑router.js
1 2 3
| import Products from './routes/Products';
<Route path="/Products" exact component={Products} />
|
4.编写UI组件文件
新建UI组件components/ProductList.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import React from 'react'; import PropTypes from 'prop-types'; import { Table, Popconfirm, Button } from 'antd';
const ProductList = ({ onDelete, products }) => { const columns = [{ title: '名称', dataIndex: 'name', }, { title: '操作', render: (text, record) => { return ( <Popconfirm title="Delete?" onConfirm={() => onDelete(record.id)}> <Button>Delete</Button> </Popconfirm> ); }, }]; return ( <Table dataSource={products} columns={columns} /> ); };
ProductList.propTypes = { onDelete: PropTypes.func.isRequired, products: PropTypes.array.isRequired, };
export default ProductList;
|
5.定义Model模型
dva通过model的概念把一个领域的模型管理起来,其中包括同步更新state的reducers,处理异步逻辑的effects,数据源订阅的subscriptions。
新建models/products.js
1 2 3 4 5 6 7 8 9
| export default { namespace: 'products', state: [], reducers: { 'delete'(state, { payload: id }) { return state.filter(item => item.id !== id); }, }, };
|
namespace 表示在全局 state上的 key
state 是初始值,在这里是空数组
reducers 等同于redux里的 reducer,接收 action,同步更新 state
6.载入模块
在index.js里面载入模块
app.model(require('./models/products').default);
7.model连接component
dva提供connect方法。编辑routes/Products.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import React from 'react'; import { connect } from 'dva'; import ProductList from '../components/ProductList';
const Products = ({ dispatch, products }) => { function handleDelete(id) { dispatch({ type: 'products/delete', payload: id, }); } return ( <div> <h2>List of Products</h2> <ProductList onDelete={handleDelete} products={products} /> </div> ); };
export default connect(({ products }) => ({ products, }))(Products);
|
最后在index.js里面初始化一些数据,一个简单的dva应用完成。
1 2 3 4 5 6 7 8
| const app = dva({ initialState: { products: [ { name: 'dva', id: 1 }, { name: 'antd', id: 2 }, ], }, });
|
8.打包发布应用
npm run build
二、dva数据流
数据改变的发生通常是通过用户的交互行为或者浏览器行为(如路由跳转)触发的,当此类行为会改变数据的时候会通过dispatch发起一个Action,如果是同步行为直接通过Reducers改变State,如果是异步行为会先触发Effects流向Reducers最后改变State。

2.1.State对象
State通常是一个JavaScript对象,表示某个模型全局的状态数据,可以在模型里面配置相关信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| export default{ namespace: 'modelname', state: { }, subscriptions: { }, effects: { }, reducer: { }, }
|
2.2.Action对象
Action是一个普通JavaScript对象,是改变state的一个行为,也是唯一改变state的途径,想要改变state,需要将Action传入dispatch函数中,该函数是在组件connect模型后通过props传入的。
1 2 3
| this.props.dispatch({type: 'modelname/xxx'}).then((v={}) => { console.log(v) })
|
2.3.dispatch方法
dispatch函数连接路由组件和模型,通过传入Action调用model中的逻辑改变state,Action只是描述了一个行为,dispatch是触发这个行为,而reducers则是如何改变这个行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| this.props.dispatch({type: 'modelname/getInfo'}).then((v={}) => { console.log(v) })
export default{ namespace: 'modelname', state: { getdate: {}, }, subscriptions: { }, effects: { *getInfo({payload}, {call, put, select}) { const data = yield call(getInfo)
if (!data) return
yield put({ type: 'change', payload: { name: 'getdate', value: data } }) return data }, }, reducer: { change(state, action) { const {payload} = action if (!payload || !payload.name) return
return { ...state, [payload.name]: payload.value } } }, }
|
2.4.connect方法
connect函数路由使组件和模型联系起来,可以在connect函数里面传入mapStateToProps函数,用于把model中的state和组件的props一一映射,mapStateToProps函数接收state作为参数,返回一个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13
| class MyComponent extends component{ }
const mapStateToProps = state => { return { data1: state.modelname.data1, data2: state.modelname.data2, } }
export default connect(mapStateToProps)(MyComponent);
|
2.5.Reducers
reducers可以同步直接更改state,该设计来源于高阶函数reduce,接收两个参数:之前已经累积运算的结果和当前要被累积的值,返回的是一个新的累积结果。
示例详见2.3小结,通过put方法调用reducers的change函数改变state。
2.6.Effects
effects可以调用异步操作,获取的异步数据流向reducers然后改变state,由于采用了generator的相关概念,所以将异步转成同步写法,从而将effects转为纯函数。示例详见2.3小结,函数*getInfo
就是一个generator函数的形式,
payload是dispatch调用action时传入的参数对象,第二个参数为方法的集合,分别是call、put、select。
call以异步的方式调用异步函数,第二个参数对象可选
const data = yield call(asyncFn, ...args)
put用于触发action
yield put({ type: 'change', payload: {name: 'getdate', value: data}})
select用于从state中获取数据
const list = yield select(state => state.list)
2.7.subscriptions
subscriptions可以监听数据源,可以简单理解为一个监听器,可以监听路由变化,鼠标,键盘变化,服务器连接变化,状态变化等,这样在其中就可以根据不同的变化做出相应的处理。例如监听路由和click事件:
1 2 3 4 5 6 7 8 9 10 11 12
| subscriptions: { onClick ({dispatch}) { document.addEventListener('click',() => { dispatch (type:"handleClick") }) }, setupHistory({dispatch,history}){ history.listen((location) => { console.log(location) }) }, }
|