1. npm install —save dva dva-loading
  2. 声明model,model中有reducerseffects

    1. const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
    2. // 注册 Model
    3. app.model({
    4. namespace: 'count', // 当前model的名称
    5. state: {
    6. count: 0,
    7. num: 77,
    8. }, // 当前model的状态
    9. reducers: { // 处理同步active
    10. add(state, active) {
    11. const { payload } = active;
    12. payload.count += 1;
    13. console.log(state, active);
    14. return {
    15. ...state,
    16. ...payload,
    17. }
    18. },
    19. minus(state, active) {
    20. const { payload } = active;
    21. payload.count -= 1;
    22. console.log(state, active);
    23. return {
    24. ...state,
    25. ...payload,
    26. }
    27. },
    28. },
    29. effects: { // 处理异步active
    30. * addAfter1Second(action, { call, put }) {
    31. console.log(action);
    32. yield call(delay, 10000);
    33. yield put({ type: 'add', payload: action.payload });
    34. },
    35. * minusAfter1Second(action, { call, put }) {
    36. console.log(action);
    37. yield call(function() {
    38. console.log('222')
    39. }, 10000);
    40. yield put({ type: 'minus', payload: action.payload });
    41. },
    42. },
    43. subscriptions: {
    44. setup({ history, dispatch }) {
    45. // 监听 history 变化,当进入 `/` 时触发 `load` action
    46. console.log(history)
    47. return history.listen(
    48. ({ pathname }) => {
    49. console.log(pathname);
    50. if (pathname === '/') {
    51. dispatch({ type: 'load' });
    52. }
    53. }
    54. );
    55. },
    56. },
    57. });
  3. Action 是一个普通 javascript 对象,它是改变 State 的唯一途径。无论是从 UI 事件、网络回调,还是 WebSocket 等数据源所获得的数据,最终都会通过 dispatch 函数调用一个 action,从而改变对应的数据。action 必须带有 type 属性指明具体的行为,其它字段可以自定义,如果要发起一个 action 需要使用 dispatch 函数;需要注意的是 dispatch 是在组件 connect Models以后,通过 props 传入的。

上面那个model就有4个active add,minus,addAfter1Second,minusAfter1Second

  1. dispatch 触发active,dipatcch会触发Reducer函数 Reducer = (state: S, action: A) => S,会进行状态的合并

    1. props.dispatch({
    2. type: 'count/add',
    3. payload: {
    4. count: props.count.count
    5. }
    6. })
  2. Effect 副作用,底层使用redux-sage做异步流程控制,采用generator机制

  3. Subscriptions 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。
  4. 配置router
  5. connect Model

image.png

  1. import * as React from 'react';
  2. import dva, { connect } from 'dva';
  3. import createLoading from 'dva-loading'; // dva自动处理loading的插件
  4. import {Router, Route, Switch} from 'dva/router'; // dva里嵌套的react-router
  5. const app = dva();
  6. console.log(app._store); // 顶部的 state 数据
  7. app.use(createLoading());
  8. const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
  9. // 注册 Model
  10. app.model({
  11. namespace: 'count', // 当前model的名称
  12. state: {
  13. count: 0,
  14. num: 77,
  15. }, // 当前model的状态
  16. reducers: { // 处理同步active
  17. add(state, active) {
  18. const { payload } = active;
  19. payload.count += 1;
  20. console.log(state, active);
  21. return {
  22. ...state,
  23. ...payload,
  24. }
  25. },
  26. minus(state, active) {
  27. const { payload } = active;
  28. payload.count -= 1;
  29. console.log(state, active);
  30. return {
  31. ...state,
  32. ...payload,
  33. }
  34. },
  35. },
  36. effects: { // 处理异步active
  37. * addAfter1Second(action, { call, put }) {
  38. console.log(action);
  39. yield call(delay, 10000);
  40. yield put({ type: 'add', payload: action.payload });
  41. },
  42. * minusAfter1Second(action, { call, put }) {
  43. console.log(action);
  44. yield call(function() {
  45. console.log('222')
  46. }, 10000);
  47. yield put({ type: 'minus', payload: action.payload });
  48. },
  49. },
  50. subscriptions: {
  51. setup({ history, dispatch }) {
  52. // 监听 history 变化,当进入 `/` 时触发 `load` action
  53. console.log(history)
  54. return history.listen(
  55. ({ pathname }) => {
  56. console.log(pathname);
  57. if (pathname === '/') {
  58. dispatch({ type: 'load' });
  59. }
  60. }
  61. );
  62. },
  63. },
  64. });
  65. // 3. View
  66. const Home = connect(count => count)(function(props) {
  67. console.log(props)
  68. return (
  69. <div>
  70. <h2>{ props.count.count }</h2>
  71. <h2>{ String(props.loading.global) }</h2>
  72. <button key="add" onClick={() => { props.dispatch({
  73. type: 'count/add',
  74. payload: {
  75. count: props.count.count
  76. }
  77. })
  78. }}>+</button>
  79. <button key="minus" onClick={() => { props.dispatch({
  80. type: 'count/minus',
  81. payload: {
  82. count: props.count.count
  83. }
  84. })}}>-</button>
  85. <button key="*add" onClick={() => {
  86. props.dispatch({
  87. type: 'count/addAfter1Second',
  88. payload: {
  89. count: props.count.count
  90. }
  91. })}}>+</button>
  92. <button key="*minus" onClick={() => { props.dispatch({
  93. type: 'count/minusAfter1Second',
  94. payload: {
  95. count: props.count.count
  96. }
  97. })}}>-</button>
  98. </div>
  99. );
  100. });
  101. const router = function (arg: any) {
  102. return (
  103. <Router history={arg!.history}>
  104. <Switch>
  105. <Route path="/" exact component={Home} />
  106. </Switch>
  107. </Router>
  108. )
  109. }
  110. app.router(router);
  111. app.start('#main');

注意 :dva 可以 支持 HMR,基于 babel-plugin-dva-hmr 实现 components、routes 和 models 的 HMR

image.png

generator

image.png