redux、redux-saga 和 react-router 的轻量级前端框架。(Inspired by elm and choo)
dispatch({
type: 'user/add', // 如果在 model 外调用,需要添加 namespace
payload: {}, // 需要传递的信息
});
create-react-app dva-app
cd dva-app
cnpm i dva keymaster -S
官方推荐的:
├── /mock/           # 数据mock的接口文件
├── /src/            # 项目源码目录
│ ├── /components/   # 项目组件
│ ├── /routes/       # 路由组件(页面维度)
│ ├── /models/       # 数据模型
│ ├── /services/     # 数据接口
│ ├── /utils/        # 工具函数
│ ├── route.js       # 路由配置
│ ├── index.js       # 入口文件
│ ├── index.less    
│ └── index.html    
├── package.json     # 定义依赖的pkg文件
└── proxy.config.js  # 数据mock配置文件
| 用法 | 说明 | 
|---|---|
| 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 dva,{connect} from 'dva';
import keymaster from 'keymaster';
import { Router, Route } from 'dva/router';
//dva react react-dom redux redux-saga react-router react-router-dom history 
const app = dva();
//redux combineReducers reducer都有自己的状态 
//combineReducers({counter:counterReducer})
//总的状态树 state={counter:0,counter2:0}
const delay = (millseconds)=>{
    return new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve();
        },millseconds);
    });
}
app.model({
    namespace:'counter',
    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};
        }
    }
});
const Counter = (props)=>{
    return (
        <div>
            <p>{props.number}</p>
            <button onClick={()=>props.dispatch({type:'counter/add'})}>add</button>
            <button onClick={()=>props.dispatch({type:'counter/asyncAdd'})}>asyncAdd</button>
            <button onClick={()=>props.dispatch({type:'counter/minus'})}>-</button>
        </div>
    )
}
const Counter2 = (props)=>{
    return (
        <div>
            <p>{props.number}</p>
            <button onClick={()=>props.dispatch({type:'counter2/add'})}>+</button>
            <button onClick={()=>props.dispatch({type:'counter2/minus'})}>-</button>
        </div>
    )
}
//{counter1:{number:0},counter2:{number:0}}
const ConnectedCounter = connect(
    state=>state.counter
)(Counter);
const ConnectedCounter2 = connect(
    state=>state.counter2
)(Counter2);
app.router(
    ({app,history})=>(
        <Router history={history}>
            <>
            <Route path="/counter1" component={ConnectedCounter}/>
            <Route path="/counter2" component={ConnectedCounter2}/>
            </>
        </Router>
    )
);
app.start('#root');
$ npm run build