一、Dva简介
- 首先Dva是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,Dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。
欲知详情请查看Dva官网
二、创建dva工程
通过npm安装dva-cli脚手架(npm的安装就省略了,作为一个前端开发人员必备的),执行npm install dva-cli -g
再通过dva脚手架新建工程,执行dva new dva-app
-
运行项目,打开工程执行npm start,运行成功后如下图:
-
工程主要目录结构介绍,工程目录结构如下图:
- asserts 用于存放静态资源,跟以往正常的java工程一样,里面扔图片视频之类的。
- components 用于存放公共组件,比如页面头尾导航条。
- routes 用于存放路由组件,可以通俗的理解成页面。与component的区别在于一般是非公共的并且会跟model里的数据模型联系起来。
- models 用于存放模型文件,model是react-redux体系里比较重要的部分,state以及reducer都会写在里面,同时,通过connect跟routes里面的组件进行绑定。routes中的组件通过connect拿到model中的数据,model也能通过connect接收到组件中dispatch的action。
- services 用于存放跟后台交互的服务文件,一般都需要调用后台的接口。
- utils 用于存放工具类库。
三、组件通信的例子
步骤1
class Son extends React.Component {
render() {
return <input/>;
}
}
class Father extends React.Component {
render() {
return <div>
<Son/>
<p>这里显示 Son 组件的内容</p>
</div>;
}
}
ReactDOM.render(<Father/>, mountNode);
看这个例子,想一想父组件如何拿到子组件的值。
步骤2
class Son extends React.Component {
render() {
return <input onChange={this.props.onChange}/>;
}
}
class Father extends React.Component {
constructor() {
super();
this.state = {
son: ""
}
}
changeHandler(e) {
this.setState({
son: e.target.value
});
}
render() {
return <div>
<Son onChange={this.changeHandler.bind(this)}/>
<p>这里显示 Son 组件的内容:{this.state.son}</p>
</div>;
}
}
ReactDOM.render(<Father/>, mountNode);
看下这个例子,看懂源码,理解子组件如何通过父组件传入的函数,将自己的值再传回父组件。
四、数据流问题
目前流行的数据流方案有:
到底哪一种架构最合适 React ?
五、目前最流行的数据流方案
截止 2017.1,最流行的社区 React 应用架构方案如下。
- 路由: React-Router
- 架构: Redux
- 异步操作: Redux-saga
缺点:要引入多个库,项目结构复杂。
六、dva 是什么
dva 是体验技术部开发的 React 应用框架,将上面三个 React 工具库包装在一起,简化了 API,让开发 React 应用更加方便和快捷。
dva = React-Router + Redux + Redux-saga
七、dva 应用的最简结构
import dva from 'dva';
const App = () => <div>Hello dva</div>;
// 创建应用
const app = dva();
// 注册视图
app.router(() => <App />);
// 启动应用
app.start('#root');
八、数据流图
九、核心概念
- State:一个对象,保存整个应用状态
- View:React 组件构成的视图层
- Action:一个对象,描述事件
- connect 方法:一个函数,绑定 State 到 View
- dispatch 方法:一个函数,发送 Action 到 State
State 是储存数据的地方,收到 Action 以后,会更新数据。
View 就是 React 组件构成的 UI 层,从 State 取数据后,渲染成 HTML 代码。只要 State 有变化,View 就会自动更新。
Action 是用来描述 UI 层事件的一个对象。
{
type: 'click-submit-button',
payload: this.form.data
}
connect 是一个函数,绑定 State 到 View。
import { connect } from 'dva';
function mapStateToProps(state) {
return { todos: state.todos };
}
connect(mapStateToProps)(App);
connect 方法返回的也是一个 React 组件,通常称为容器组件。因为它是原始 UI 组件的容器,即在外面包了一层 State。
connect 方法传入的第一个参数是 mapStateToProps 函数,mapStateToProps 函数会返回一个对象,用于建立 State 到 Props 的映射关系。
dispatch 是一个函数方法,用来将 Action 发送给 State。
dispatch({
type: 'click-submit-button',
payload: this.form.data
})
dispatch 方法从哪里来?被 connect 的 Component 会自动在 props 中拥有 dispatch 方法。
connect 的数据从哪里来?
十、dva 应用的最简结构(带 model)
// 创建应用
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 />);
// 启动应用
app.start('#root');
app.model
dva 提供 app.model 这个对象,所有的应用逻辑都定义在它上面。
const app = dva();
// 新增这一行
app.model({ /**/ });
app.router(() => <App />);
app.start('#root');
Model 对象的例子
{
namespace: 'count',
state: 0,
reducers: {
add(state) { return state + 1 },
},
effects: {
*addAfter1Second(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'add' });
},
},
}
Model 对象的属性
- namespace: 当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成
- state: 该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出
- reducers: Action 处理器,处理同步动作,用来算出最新的 State
- effects:Action 处理器,处理异步动作
十一、 Reducer
Reducer 是 Action 处理器,用来处理同步操作,可以看做是 state 的计算器。它的作用是根据 Action,从上一个 State 算出当前 State。
一些例子:
// count +1
function add(state) { return state + 1; }
// 往 [] 里添加一个新 todo
function addTodo(state, action) { return [...state, action.payload]; }
// 往 { todos: [], loading: true } 里添加一个新 todo,并标记 loading 为 false
function addTodo(state, action) {
return {
...state,
todos: state.todos.concat(action.payload),
loading: false
};
}
Effect
Action 处理器,处理异步动作,基于 Redux-saga 实现。Effect 指的是副作用。根据函数式编程,计算以外的操作都属于 Effect,典型的就是 I/O 操作、数据库读写。
function *addAfter1Second(action, { put, call }) {
yield call(delay, 1000);
yield put({ type: 'add' });
}
Generator 函数
Effect 是一个 Generator 函数,内部使用 yield 关键字,标识每一步的操作(不管是异步或同步)。
call 和 put
dva 提供多个 effect 函数内部的处理函数,比较常用的是 call
和 put
。
- call:执行异步函数
- put:发出一个 Action,类似于 dispatch