[toc]
这个 Demo 来自 dva 脚手架的参考文档
克隆项目文件:
git clone https://github.com/Ke-Di/dva-demo.git
# 或
git clone git@github.com:Ke-Di/dva-demo.git
进入目录安装依赖:
开始前请确保没有安装roadhog、webpack到NPM全局目录, 国内用户推荐yarn或者cnpm
yarn 或者 npm i
开发:
yarn start 或者 npm run start
打开 http://localhost:8000 #端口在package.json中cross-env后加上 PORT=8000指定
打包:
yarn build 或者 npm run build
# 将打包产生的 /dist 文件夹的内容传入服务器并监听前端服务即可
React
本身只是一个 DOM
的抽象层,使用组件构建虚拟 DOM
。
如果开发大应用,还需要解决以下问题:
- 通信:组件之间如何通信?
- 数据流:数据如何和视图串联起来?路由和数据如何绑定?如何编写异步逻辑?等等
组件会发生三种通信:
- 向子组件发消息
- 向父组件发消息
- 向其他组件发消息
React 只提供了一种通信手段:传参。对于大应用,很不方便。
目前流行的数据流方案有:
Flux
,单向数据流方案,以Redux
为代表Reactive
,响应式数据流方案,以Mobx
为代表- 其他,比如
rxjs
等
到底哪一种架构最合适 React
?
目前最流行的数据流方案,截止 2017.1,最流行的社区 React
应用架构方案如下:
- 路由:
React-Router
- 架构:
Redux
- 异步操作:
Redux-saga
缺点:要引入多个库,项目结构复杂。
dva
是体验技术部开发的 React
应用框架,将上面三个 React
工具库包装在一起,简化了 API,让开发 React
应用更加方便和快捷。
dva = React + React-Router + Redux + Redux-saga
State
:一个对象,保存整个应用状态State
是储存数据的地方,收到Action
以后,会更新数据。View
:React
组件构成的视图层View
就是 React 组件构成的 UI 层,从State
取数据后,渲染成 HTML 代码。只要 State 有变化,View
就会自动更新。Action
:一个对象,描述事件Action
是用来描述 UI 层事件的一个对象。
{
type: 'click-submit-button',
payload: this.form.data
}
connect
方法:一个函数,绑定State
到View
connect
是一个函数,绑定State
到View
。connect
方法返回的也是一个React
组件,通常称为容器组件。因为它是原始 UI 组件的容器,即在外面包了一层State
。connect
方法传入的第一个参数是mapStateToProps
函数,mapStateToProps
函数会返回一个对象,用于建立State
到Props
的映射关系。
import { connect } from 'dva';
const mapStateToProps = (state) => {
return { todos: state.todos };
}
connect(mapStateToProps)(App);
dispatch
方法:一个函数,发送Action
到State
dispatch
是一个函数方法,用来将Action
发送给State
。dispatch
方法从哪里来?被connect
的Component
会自动在props
中拥有dispatch
方法。
dispatch({
type: 'click-submit-button',
payload: this.form.data
})
dva
提供 app.model
这个对象,所有的应用逻辑都定义在它上面。
const app = dva();
// 新增这一行
app.model({ /**/ });
app.router(() => <App />);
app.start('#root');
Model
对象的例子
import key from "keymaster";
app.model({
namespace: 'count',
state: {
record: 0,
current: 0,
},
reducers: {
add(state) {
const newCurrent = state.current + 1;
return { ...state,
record: newCurrent > state.record ? newCurrent : state.record,
current: newCurrent,
};
},
minus(state) {
return { ...state, current: state.current - 1};
},
},
effects: {
*add(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'minus' });
},
},
subscriptions: {
keyboardWatcher({ dispatch }) {
key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });
},
},
});
const delay = timeout => {
return new Promise(resolve => {
setTimeout(resolve, timeout);
});
};
Model
对象的属性
namespace
: 当前Model
的名称。整个应用的State
,由多个小的Model
的State
以namespace
为key
合成state
: 该Model
当前的状态。数据保存在这里,直接决定了视图层的输出reducers
:Action
处理器,处理同步动作,用来算出最新的State
effects
:Action
处理器,处理异步动作*add() {}
等同于add: function*(){}
call
和put
都是redux-saga
的effects
,call
表示调用异步函数,put
表示 dispatch action,其他的还有select
,take
,fork
,cancel
等,详见 redux-saga 文档- 默认的
effect
触发规则是每次都触发(takeEvery
),还可以选择takeLatest
,或者完全自定义take
规则 Subscription
语义是订阅,用于订阅一个数据源,然后根据条件dispatch
需要的action
。数据源可以是当前的时间、服务器的websocket
连接、keyboard
输入、geolocation
变化、history
路由变化等等。
Model
对象的优点
- 把
store
及saga
统一为一个model
的概念, 写在一个 js 文件里面 - 增加了一个
Subscriptions
, 用于收集其他来源的action
, eg: 键盘操作 model
写法很简约, 类似于 DSL 或者 RoR, coding 快得飞起✈️