1. dva #

2.数据流向 #

dva-flow

3. 8个概念 #

3.1 State #

3.2 Action #

3.3 dispatch #

3.4 Reducer #

3.5 Effect #

3.6 Subscription #

3.7 Router #

3.8 Route Components #

4. 初始化环境 #

create-react-app dva-app
cd dva-app
cnpm i dva keymaster -S

5. 文件结构 #

官方推荐的:

├── /mock/           # 数据mock的接口文件
├── /src/            # 项目源码目录
│ ├── /components/   # 项目组件
│ ├── /routes/       # 路由组件(页面维度)
│ ├── /models/       # 数据模型
│ ├── /services/     # 数据接口
│ ├── /utils/        # 工具函数
│ ├── route.js       # 路由配置
│ ├── index.js       # 入口文件
│ ├── index.less    
│ └── index.html    
├── package.json     # 定义依赖的pkg文件
└── proxy.config.js  # 数据mock配置文件

6. 计数器 #

用法 说明
app = dva(opts) 创建应用,返回 dva 实例
app.use(hooks) 配置 hooks 或者注册插件
app.model(model) 注册 model
app.router(({ history, app }) => RouterConfig) 注册路由表
app.start(selector?) 启动应用。selector 可选
import React from 'react';
import { Dispatch } from 'redux';
import dva, { connect } from 'dva';
import keymaster from 'keymaster';
import { RouterAPI } from 'dva';
import { Router, Route } from 'dva/router';
interface Counter1State {
    number: 0
}
interface Counter2State {
    number: 0
}
interface CombinedState {
    counter1: Counter1State;
    counter2: Counter2State;
}
const app = dva();
const delay = (millseconds: number) => {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, millseconds);
    });
}
app.model({
    namespace: 'counter1',
    state: { number: 0 },
    reducers: {//接收老状态,返回新状态
        add(state) { //dispatch({type:'add'});
            return { number: state.number + 1 };
        },
        minus(state) {//dispatch({type:'minus'})
            return { number: state.number - 1 };
        }
    },
    // 延时操作 调用接口  等待
    effects: {
        *asyncAdd(action, { put, call }) { //redux-saga/effects {put,call}
            yield call(delay, 1000);//把100传给delay并调用,yield会等待promise完成
            yield put({ type: 'add' });
        }
    },
    subscriptions: {
        keyboard({ dispatch }) {
            keymaster('space', () => {
                dispatch({ type: 'add' });
            });
        },
        changeTitle({ history }) {
            setTimeout(function () {
                history.listen(({ pathname }) => {
                    document.title = pathname;
                });
            }, 1000);

        }
    }
});
app.model({
    namespace: 'counter2',
    state: { number: 0 },
    reducers: {//接收老状态,返回新状态
        add(state) { //dispatch({type:'add'});
            return { number: state.number + 1 };
        },
        minus(state) {//dispatch({type:'minus'})
            return { number: state.number - 1 };
        }
    }
});
type Counter1Props = Counter1State & { dispatch: Dispatch };
const Counter1 = (props: Counter1Props) => {
    return (
        <div>
            <p>{props.number}</p>
            <button onClick={() => props.dispatch({ type: 'counter1/add' })}>add</button>
            <button onClick={() => props.dispatch({ type: 'counter1/asyncAdd' })}>asyncAdd</button>
            <button onClick={() => props.dispatch({ type: 'counter1/minus' })}>-</button>
        </div>
    )
}
type Counter2Props = Counter2State & { dispatch: Dispatch };
const Counter2 = (props: Counter2Props) => {
    return (
        <div>
            <p>{props.number}</p>
            <button onClick={() => props.dispatch({ type: 'counter2/add' })}>+</button>
            <button onClick={() => props.dispatch({ type: 'counter2/minus' })}>-</button>
        </div>
    )
}

const mapStateToProps1 = (state: CombinedState): Counter1State => state.counter1;
const ConnectedCounter = connect(
    mapStateToProps1
)(Counter1);
const mapStateToProps2 = (state: CombinedState): Counter2State => state.counter2;
const ConnectedCounter2 = connect(
    mapStateToProps2
)(Counter2);
app.router(
    (api?: RouterAPI) => {
        let { history } = api!;
        return (
            (
                <Router history={history}>
                    <>
                        <Route path="/counter1" component={ConnectedCounter} />
                        <Route path="/counter2" component={ConnectedCounter2} />
                    </>
                </Router>
            )
        )
    }
);
app.start('#root');

7 构建应用 #

$ npm run build

8. 参考 #