model 是 dva 中最重要的概念,一共包含了5个属性。在上篇的例子中,我们用到了dispatch, reducer, namespace,state等概念。接下我们来逐一分析。

    • Namespace

    namespace 作为当前 model 的名称,必须保证全局唯一性,整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成。获取state时都会用到 namespace。

    1. ...
    2. namespace: 'counter',
    3. ...
    • State

    state 表示 model 的状态数据,可以是任何值,但在业务当中通常会设置为一个对象来使用。

    1. const defaultState = {
    2. count: 0,
    3. }
    4. ...
    5. state: {
    6. ...defaultState
    7. }
    8. ...

    state 中通常会设置初始值,但是其优先级会低于 dva()中设置的值,这时候 count 初始值为2。

    1. const app = dva({
    2. initialState: {
    3. counter: {
    4. count: 2
    5. }
    6. },
    7. });

    state的改变有两种方式,一种 dispatch 方法发送 action,一种 put 方法发送 action 。最终都是通过触发 reducer 来改变 state。

    • Reducer

    state 的每一次数据变更最终都是通过 reducer 来改变 state。reducer接收两个参数:一个是当前的 state 值,一个是需要改变的值,最终返回的是一个新值。该函数把一个集合归并成一个单值,值得注意的得是 reducer 必须是纯函数,所以同样的输入必然得到同样的输出,它们不应该产生任何副作用。

    1. ...
    2. reducers: {
    3. save(state, action) {
    4. return {
    5. ...state,
    6. ...action.payload
    7. };
    8. },
    9. reset(state){
    10. return {
    11. ...state,
    12. ...defaultState
    13. }
    14. }
    15. }
    16. ...
    • Effect

    effect 是一个 Generator 函数,内部使用 yield 关键字,标识每一步的操作,effect 主要用于异步流程的控制和业务逻辑的处理。effect 有三个方法,分别为call,put,select。call 用于数据异步请求的,put 用于发送 action 改变 state (相当于 dispatch 方法),select 方法用于获取不同 model 中的state的值。该例子属于 dva-demoTwo

    1. import {fetchList} from '../services/example';
    2. ...
    3. effects: {
    4. *fetch({ payload }, { call, put, select }) { // eslint-disable-line
    5. const list = yield select(state => state.list);
    6. console.log(list)
    7. const {data} = yield call(fetchList);
    8. yield put({
    9. type: 'save',
    10. payload: {data: data.list}
    11. })
    12. },
    13. }
    14. ...
    • Subscription

    subscription 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。写法如下:

    1. ...
    2. subscriptions: {
    3. setup({ dispatch, history }) {
    4. return history.listen(({pathname}) => {
    5. if (pathname.includes('count')) {
    6. console.log('dispatch')
    7. // dispatch({···});
    8. }
    9. });
    10. },
    11. },
    12. ...
    • Dispatch

    Dispatch是一个用于发送 action 的函数,在上面我们提到过 action 是改变 state 的唯一途径,diapatch 就是触发这个改变的方法。常用用法:

    1. dispatch({
    2. type: 'counter/save’, // 如果在model外,则要加上namespace
    3. payload: {count : count - 1} // 需要改变的值
    4. })

    在了解概念之后,结合下图 dva 中的数据流转则一目了然。当路由跳转或者由用户交互行为引起的行为,都会通过 UI 视图或者 subscription 触发 dispatch 发起一个 action,如果是异步行为会先出发 effect 然后流向 reducers 最终改变 state,如果是同步行为,则直接触发 reducers 改变 state。state 的改变会导致UI视图页面重新渲染。

    PPrerEAKbIoDZYr.png