dva是支付宝前端团队时常维护的一个开源项目,会维护,维护,维护!!它集成了redux , react-router , 可能还用到了TypeScript等等,看github上说是基于react的最佳实践,使用下来确实感觉挺不错的。组件我没有用antd而是自己写的。
构建项目
首先你需要安装node我想应该前端都会用吧,当然react你也必须会。如果不会react和前端基础那估计你要去补一补了咯
1. 安装dva-cli
npm install dva-cli -g
2. 初始化dva项目
dva new newDva
进入项目目录执行npm start 就可以看到你初始化的项目了
3. 文件目录
routes 和 models 两个文件夹是最重要的两个里面会有大量内容
utils 里面放的基本上是工具函数自己封装或者别的地方拿
services 里面基本是一些网络请求的api 在 models文件里面的 effects 内会用到
assets 里面会有一些静态的图片之类的
整个app 主要的逻辑加上组件都在src这个大的文件夹下面
mock 里面就是模拟的api接口
4. dva指令
1. npm start
启动项目,自动检测文件变化热更新页面。
2. npm build
打包项目。会在文件名后加上一个hash值并且压缩文件
3. dva g route example
创建一个路由,并且在router.js中自动注册这个路由。其实route目录下放的就是所有页面组件,他创建的就是一个页面。
会有两个文件,个人建议不用这个指令,手动创建文件夹作为一个页面,当前页面的所有组件放在这个文件夹里面,公共组件可以放在components文件夹下
import React from 'react';
import { connect } from 'dva';
import styles from './IndexPage.css';
function IndexPage() {
return (
....
);
}
IndexPage.propTypes = {
};
export default connect()(IndexPage);
这是初始化之后的首页组件模板,dva提倡状态组件。大致就是你无法在一个dva组件里面使用生命周期。
参数状态有对应的model去控制,函数类型组件直接返回html模板可能是一种对react的性能优化,当然你也可以在return中嵌套别的自定义组件。return <OtherComponent />
一个顶级标签的规矩还是不能破坏的
在这里面css可以看到是以一个对象形式引入,其实是dva采用了css-model这种方式。具体的可以去看css-model。使用写法是这样:<OtherComponent className={style.one} />
如果你要多个className 那你需要这样写<OtherComponent className={style.one + " " + style.another} />
4. dva g model example
这个指令非常好用,他会创建一个example.js的文件作为一个模型,并且在router.js中自动注册这个模型。app.model(require('./models/example'));
这里的一个模型应该和route里面的文件夹一一对应起来,相当于平时使用的redux。里面有当前这个路由组件的state,具体是长这个样子的
export default {
namespace: 'example',
state: {},
subscriptions: {
setup({ dispatch, history }) { // eslint-disable-line
},
},
effects: {
*fetch({ payload }, { call, put }) { // eslint-disable-line
yield put({ type: 'save' });
},
},
reducers: {
save(state, action) {
return { ...state, ...action.payload };
},
},
};
namespace 是这个model的命名,他是一个全局的,只能出现一个,而且最好是和组件对应,
state是这个model里面的数据,也是他对应组件的state
dispatch 是触发方法的一个函数,在model里面使用可以使用
subscriptions:我认为相当于组件的所有生命周期都可以在这里找到对应的api去调用,常用是setup在组件渲染之前会触发,setup一般是直接出发effects里面的方法
> dispatch({ type: 'fetch'}); //如果是当前model可以直接写方法名
如果是别的model的那你需要前面加上model名字 { type: 'OtherModel/fetch'}
effects:所有网络请求都应该放在这里面,应为它使用了generator来处理异步,这里是唯一一个地方可以处理异步请求的。当然你要大牛可以自己在别的地方写咯。
- 在fetch这个函数里面传入得第二个对象 { call, put },是定义好的api可以去看官方文档;
- call方法里面可以传入一个方法,和一个参数,他会自动执行这个方法,一般我们就把网络请求放在这里面
const data = yield call( apiFunction );
拿到请求回来的数据了; - 调用
const data = yield put( { type : "save" , payload :{data} );
调用当前组件的save方法存入当前的state,其实这里和subscriptions中的dispatch方法类似; - 那么其实所有model的state都是共享的你可以在其他model中拿到这个model的state,或是所有model的state
const data = yield select( { modelName } => modelName.stateValue );
- 组件间的跳转也应当在这里
import { routerRedux } from 'dva/router'; yield put( routerRedux.push("/targit"));
reducers:这里面放的全是改变state的函数传入两个参数,当前model的state和数据,在这里利用传入的数据对state进行修改。一般会在effects里面被调用,或者是在和这个模块绑定的组件里面通过事件回调,
dispatch({ type: 'save'})
在组件中使用model
通过函数参数方式传入
导出涌dva的connect方法连接组件和model;
const IndexPage = ({ dispatch, example })=> {
return <Index />
}
export default connect(({ example }) => ({
example
}))(IndexPage);
那么你在这个组件里面就可以拿到example这个model的所有state和dispatch这个方法
state:example.stateName获取这个model的一个state属性值
为了能在组件中触发model的方法做一系列的操作那么就需要使用dispatch了
在组件事件中触发
function(){ dispatch({ type : "model/name" payload : "" }) }
触发了这个model的name方法做了一系列操作;
如果你的组件必须要用到生命周期
那你也可以用class 来声明一个组件
class AppointmentPage extends React.Component {
handleClick(){
this.props.dispatch({
type: 'modelName/name'
})
}
render(){
return (...)
}
}
export default connect(({ modelName }) => ({
modelName
}))(Component);
dispatch方法会在this.props里面。
5.如何模拟后台接口返回数据
我采用了nodejs作为本地服务器
需要在.roadhogrc文件中添加
"proxy": {
"/api": {
"target": "http://127.0.0.1:3000/api/",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
},
让他去代理你的后台服务那么你在请求的时候地址只需要写 “api/...” 推荐使用fetch。
总结
由于之前开源协议的原因可能会用很多大佬放弃react什么的啊,但是我觉得如果是做开源项目完全没必要,企业内部呢厉害的应该会自己封装类似的,大部分服务于国内的项目其实没什么必要换,Facebook根本进不来嘛,而且这样的思路值得学习。
dva的使用一开始可能会非常不适应,但是写过之后感觉还是非常nice的结构清晰,写法也不复杂,事件处理都封装的不错,特别是在redux这里的封装,使用起来非常舒服,如果自己写估计就不是这么一点复杂度了,命令行指令善用。dva用了较多的解构赋值箭头语法generator等等,不会的要多看es6。放一张图帮助理解
图是偷来的。。
如果有什么建议或者不对的可以讨论啊!!!