dva

3 5月

dva本质上是react,react-redux,redux-saga,react-router的封装。通常项目中以页面维度设计Container Components,在dva中约束为Route Components,即connect的都是Route Components,所以都在/routes/目录下。

数据流向并没有什么特殊,同步行为会直接通过Reducers改变State,异步行为会触发Effects,然后流向Reducers改变State:

  • dva对象
  • Models
  • Router
  • 动态加载
  • 其他
    • CSS Modules
    • 代理
    • Mock
    • 其他API

dva对象

dva(opts)对象里包含了封装的各对象,opts有两个常用属性:history默认是hashHistory,initialState初始化state,默认是{}。

你可以通过它获取到当前state(通常项目中不会这么做)

import dva from 'dva';
import createHistory from 'history/createBrowserHistory';

// 1. Initialize
const app = dva({
    history: createHistory(),  // 默认是hashHistory,可用改成browserHistory
    initialState: {
        ...
    },
});

// 2. Plugins
// app.use({});

// 3. Model
app.model(require('./models/products').default);

// 4. Router
app.router(require('./router').default);

// 5. Start
app.start('#root');

console.log(app._store.getState());

opts还有onError,onAction,onStateChange,onReducer,onEffect,onHmr,extraReducers,extraEnhancers hooks供配置使用,具体点这里

Models

dva通过model来管理state。

namespace: 当前Model的名称。State由多个Model的State(以namespace为key)合成。在当前model中触发action时不需要写namespace,例如{ type: ‘save’ },但在组件中触发action时需要带上namespace,例如在某个组件应该是{ type: ‘user/fetch’ }。

state: 当前Model的state

reducers: 处理同步动作,返回最新的state

effects:处理异步动作,常用call执行异步函数,put发出一个action,类似于dispatch,take操作来监听actionredux-saga本质上就是个middleware,拦截action,发起异步请求,根据异步请求的结果,继续向reducer发一个type === doneSucc/doneFail的action。

effects格式为*(action, effects) => void[*(action, effects) => void, { type }]

subscriptions:用于收集其他来源的action,例如键盘操作,当前时间,websocket,geolocation,history路由的变化等。格式为({ dispatch, history }, done) => unlistenFunction。subscription并没有加try…catch,所以有错误时需通过第二个参数done主动抛错。

subscriptions中注册的监听函数运行所有页面中,可以监测全局的变化, 即使和当前页面不相关model里也可以进行数据改动或者请求接口之类的。

export default {
    namespace: 'count',
    state: {    // 优先级低于传给 dva() 的 opts.initialState
        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'}) });
        },
    },
};

Router

通过浏览器提供的History API监听浏览器url的变化,实现前端路由。dva/router输出react-router接口,其中routerRedux输出react-router-redux接口。

路由配置通常是这样的:app.router(({ history, app }) => RouterConfig)

// index.js
app.router(require('./router').default);

// router.js
import React from 'react';
import { Router, Route, Switch, routerRedux } from 'dva/router';
import Products from './routes/Products';

const { ConnectedRouter } = routerRedux;

function RouterConfig({ history }) {
    return (
        <routerRedux history={history}>
            <Switch>
                <Route path="/products" exact component={Products} />
            </Switch>
        </routerRedux>
    );
}

export default RouterConfig;

动态加载

dva/dynamic实现路由的动态加载

import { Router, Route, Switch } from 'dva/router';
import dynamic from 'dva/dynamic';
import Products from './routes/Products';

function RouterConfig({ history, app }) {
    const dynamicProduct = dynamic({
        app,
        models: () => [
            import('./models/dynamicProduct'),
        ],
        component: () => import('./routes/DynamicProduct')
    });

    return (
        <Router history={history}>
            <Switch>
                <Route path="/products" exact component={Products} />
                <Route path="/" component={dynamicProduct} />
            </Switch>
        </Router>
    );
}
export default RouterConfig;

其他

CSS Modules

使用dva-cli初始化的项目默认启用了CSS Modules,可以在.webpackrc中禁用:

{
  "disableCSSModules": true
}

代理

如需开发过程中代理API接口,在.webpackrc中配置:

{
  "proxy": {
    "/api": {
      "target": "http://your-api-server",
      "changeOrigin": true
    }
  }
}

Mock

Mock功能可在.roadhogrc.mock.js中添加配置:

export default {
  'GET /api/users': { users: [{ username: 'admin' }] },
  'POST /api/users': (req, res) => { res.end('OK'); },   // 支持自定义函数
}

如果Mock数据太多,可以拆分后放到./mock文件夹中,然后在.roadhogrc.mock.js中引入。

其他API

dva的connect方法,就是react-redux的connect。connect后的组件可以在props中得到dispatch,state,location,history。

dva还提供了一些api,如dva/fetch(输出isomorphic-fetch接口),dva/saga(输出redux-saga接口)。

发表评论

电子邮件地址不会被公开。 必填项已用*标注