安装 dva-cli
$ npm install dva-cli -g
$ dva -v
dva-cli version 0.9.1
创建项目
dva new dva-quickstart//dva-quickstart == 项目名字
使用 antd
npm install antd babel-plugin-import --save
编辑 .webpackrc
,使 babel-plugin-import
插件生效。
{
+ "extraBabelPlugins": [
+ ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
+ ]
}
路由
在项目的src文件下 dva会自己创建一个router.js文件,即当前项目的路由配置。不需要自己过多配置,引入组件以及规定好path即可
路由跳转
import { Link,withRouter,routerRedux } from 'dva/router'
class Product extends React.Component {
goHomeRedux = (e)=>{
//这个也可以不用传递hittory来进行跳转
this.props.dispatch(routerRedux.push('./'))
}
goHome = (e)=> {
//如果当前组件没有被路由组件包裹 那么必须要由被包裹的父组件把history方法通过props的方式传递下来
this.props.history.push('./')
}
render(){
return (
<Link to="./">去首页</Link>
<Button type="dashed" onClick={ this.goHome }>去首页2</Button>
<Button type="dashed" onClick={ this.goHomeRedux }>去首页3</Button>
)
}
}
//withRouter 高阶函数可以让组件不用传递history也可以使用路由操作
export default withRouter(Product)
model
dva 通过 model 的概念把一个领域的模型管理起来,包含同步更新 state 的 reducers,处理异步逻辑的 effects,订阅数据源的 subscriptions 。
//这个是dva自带封装好的request请求方式
import * as api from '../services/example'
export default {
namespace:'product',//定义命名空间 用于识别分开的模块 表示在全局 state 上的 key
state :{//初始化状态数据
productList : [
{
id:1,
name:'kkk'
},
{
id:2,
name:'air'
}
]
},
reducers:{//等同于 redux 里的 reducer,接收 action,同步更新 state
//这里会做数据覆盖并更新的操作
updateList(state,action){
//state就是状态 action是参数
let currentProductList = deepClone(state)
currentProductList.productList.push(action.payload)
return currentProductList
}
},
effects:{
//generator语法糖
*updateListAsync ({ payload },{call,put} ){
yield put({
type:'updateList',
payload
})
},
//异步操作处理
*updateHttp({ payload },{call,put} ){
//payload 就是每次掉用这个方法传递的参数
/*
比如这里payload拿到的就是{id:1001}
clickProductListHTTP = ()=>{
this.props.dispatch({
type:'product/updateHttp',
payload:{
id:1001
}
})
}
*/
console.log(payload,'payload-updateHttp')
//网络请求
const result = yield call(api.getProduct,payload)
const data = result.data
console.log(data,'data-updateHttp')
if(data){
yield put({
type:'updateList',
payload:data
})
}
}
},
subscriptions: {
//这个函数叫什么名字都可以
setup({ dispatch, history }) { // eslint-disable-line
/*
dispatch 掉用reducers中定义的事件
history 就是路由值
{
hash: ""
pathname: "/ProductPage"
search: ""
state: undefined
}
*/
const curObj = {
name:'curObj',
id:6
}
dispatch({
type:'updateList',
payload:curObj
})
console.log(dispatch,'dispatch-setup')
},
// hello({ dispatch, history }){
// console.log(dispatch,'dispatch-hello')
// },
setupHistory({ dispatch, history }){
history.listen((location)=>{
console.log(location,'location')
})
},
},
}
function deepClone (arr){
console.log(arr,'arr---')
//分开写是因为写一起会报错
const _obj = JSON.stringify(arr),
cloneObj = JSON.parse(_obj)
return cloneObj
}
在定义这个模块之后 必须要在最外层的index.js引入
import dva from 'dva';
const app = dva();
// 3. Model 在这里引用model default理由同router
app.model(require('./models/product').default);
//另外一种方式就是统一引入 不是单独文件一个一个引入
/*
这种统一引入的方式首先要在model定义一个总的管理全部model的js
//合并model
const context = require.context('./',false,/\.js$/);
export default context
.keys()
.filter(item=>item!=='./index.js')
.map(key=>context(key))
最后导出在最外层index.js进行引用
*/
require('./models').default.forEach(key=>app.model(key.default));
在组件中使用model
import React from 'react'
import Pcompenent from '../../components/Product'
import { connect } from 'dva'
class IndexPage extends React.Component {
render(){
// /dispatch为操作model定义的方法
const { productList,dispatch,history } = this.props
return (
<div>
IndexPage
<Pcompenent productList={ productList } dispatch={dispatch} history={history}/>
</div>
)
}
}
/*
mapStateToProps是一个函数,用于建立组件跟 store 的 state 的映射关系
传入mapStateToProps之后,会订阅store的状态改变,在每次 store 的 state 发生变化的时候,都会被调用
ownProps代表组件本身的props,如果写了第二个参数ownProps,那么当prop发生变化的时候,mapStateToProps也会被调用。
例如,当 props接收到来自父组件一个小小的改动,那么你所使用的 ownProps 参数,mapStateToProps 都会被重新计算)。
mapStateToProps可以不传,如果不传,组件不会监听store的变化,也就是说Store的更新不会引起UI的更新
*/
const mapStateToProps = (state, ownProps) => {
return {
//product这里是找到自己命名的命名空间
productList:state.product
}
}
//高阶函数进行连接
export default connect(mapStateToProps)(IndexPage);
内置mock数据
dva自带mock文件 要设置mock数据可以直接在这个文件夹下创建js文件
//在这里定义接口的方法 地址 以及返回参数
module.exports = {
"GET /api/product" : {"name":"请求的数据",id:5}
}
然后还需要在.roadhogrc.mock.js这个文件进行读取文件的配置
// export default {
// //引入mock地址
// ...require('./mock/product')
// };
import fs from 'fs';
import path from 'path';
const mock = {}
fs.readdirSync(path. join(__dirname + '/mock' )). forEach(function (file) {
if (file.match(/\.js$/)){
Object.assign(mock, require('./mock/' + file))
}
})
export default mock
这里一般配置api接口在services这个文件里进行配置
import request from '../utils/request';
export function query() {
return request('/api/users');
}
export function getProduct(){
return request('/api/product');
}
最后在某个组件内部进行请求
import * as api from '../services/example'
getHTTP = async ()=>{
let data = await api.getProduct()
console.log(data)
}
总结一下,个人感觉dva就是一个套react且轻量化的框架,内部东西很齐全,不需要过多自己从新开始搭建,内置的mock模块也很方便前后端分离进行开发,对前端自己造数据还是很友好了。model模块也很方便进行全局的状态管理个人感觉跟redux差不多便于上手,使用起来如果习惯了还是很舒服。但是requert这个感觉稍微有点鸡肋,自带的fetch配置如果遇到复杂点的项目,还是得自己重新把配置覆盖再写一遍。