关于dva框架的简单操做以及demo

关于dva的心得

因为以前都用react+redux。此次接触到dva框架简单学习下。 本篇文章主要讲解了dva框架中开发经常使用API和一些使用技巧,若是想查看更多更全面的API,请参照dva官方文档: 官方democss

简单的安装步骤

1.首先全局安装dva-cli

$ npm install -g dva-cli
复制代码

2.接着使用dva-cli建立咱们的项目文件夹,这里mydva是项目名字,本身随意。

$ dva new mydva
复制代码

3.进入mydva目录,安装依赖,执行以下操做。

$ cd myapp
$ npm start
复制代码

4.启动成功以后界面以下

5.文件目录以及分析

├── mock    // mock数据文件夹
├── node_modules // 第三方的依赖
├── public  // 存放公共public文件的文件夹
├── src  // 最重要的文件夹,编写代码都在这个文件夹下
│   ├── assets // 能够放图片等公共资源
│   ├── components // 就是react中的木偶组件
│   ├── models // dva最重要的文件夹,全部的数据交互及逻辑都写在这里
│   ├── routes // 就是react中的智能组件,不要被文件夹名字误导。
│   ├── services // 放请求借口方法的文件夹
│   ├── utils // 本身的工具方法能够放在这边
│   ├── index.css // 入口文件样式
│   ├── index.ejs // ejs模板引擎
│   ├── index.js // 入口文件
│   └── router.js // 项目的路由文件
├── .eslintrc // bower安装目录的配置
├── .editorconfig // 保证代码在不一样编辑器可视化的工具
├── .gitignore // git上传时忽略的文件
├── .roadhogrc.js // 项目的配置文件,配置接口转发,css_module等都在这边。
├── .roadhogrc.mock.js // 项目的配置文件
└── package.json // 当前整一个项目的依赖
复制代码

简单的demo,参考 官方的计算的demo

1.首先来修改 route/IndexPage.js

import React from 'react';
import { connect } from 'dva';
import styles from './IndexPage.css';

class IndexPage extends React.Component {
  render() {
    const { dispatch } = this.props;

    return (
      <div className={styles.normal}>
        <div className={styles.record}>Highest Record: 1</div>
        <div className={styles.current}>2</div>
        <div className={styles.button}>
          <button onClick={() => {}}>+</button>
        </div>
      </div>
    );
  }
}

export default connect()(IndexPage);
复制代码

2.其次来修改样式routes/IndexPage.css

.normal {
  width: 200px;
  margin: 100px auto;
  padding: 20px;
  border: 1px solid #ccc;
  box-shadow: 0 0 20px #ccc;
}
.record {
  border-bottom: 1px solid #ccc;
  padding-bottom: 8px;
  color: #ccc;
}
.current {
  text-align: center;
  font-size: 40px;
  padding: 40px 0;
}
.button {
  text-align: center;
}
button {
  width: 100px;
  height: 40px;
  background: #aaa;
  color: #fff;
}
复制代码

3.界面显示以下

4.在model 里面去处理 state ,在页面输出 model 中的 state

(1)首先咱们在index.js中将models/example.js,即将model下一行的的注释打开。
import './index.css';

import dva from 'dva';
import model from './models/example'
import router from './router'

// 1. Initialize 建立dva实列
const app = dva();

// 2. Plugins 装载插件(可选)
// app.use({});

// 3. Model 注册modal
app.model(model);

// 4. Router 配置路由
app.router(router);

// 5. Start 启动应用
app.start('#root');
复制代码
(2)接下来咱们进入 models/example.js,将namespace 名字改成 countstate 对象加上 recordcurrent 属性
export default {

  namespace: 'count',

  state: {
    record: 0,
    current: 0,
  },

  subscriptions: {
    setup({ dispatch, history }) {  // eslint-disable-lines
    },
  },

  effects: {
    *fetch({ payload }, { call, put }) {  // eslint-disable-line
      yield put({ type: 'save' });
    },
  },

  reducers: {
    save(state, action) {
      return { ...state, ...action.payload };
    },
  },

};

复制代码
(3)接着咱们来到 routes/indexpage.js 页面,经过的 mapStateToProps 引入相关的 state
import React from "react";
import { connect } from "dva";
import styles from "./IndexPage.css";

class IndexPage extends React.Component {
  render() {
    const { dispatch, count } = this.props;

    return (
      <div className={styles.normal}>
        <div className={styles.record}>Highest Record: {count.record}</div>
        {/* // 将count的record输出 */}
        <div className={styles.current}>{count.current}</div>
        <div className={styles.button}>
          <button
            onClick={() => {
            }}
          >
            +
          </button>
        </div>
      </div>
    );
  }
}

function mapStateToProps(state) {
  return { count: state.count };
} // 获取state

export default connect(mapStateToProps)(IndexPage);
复制代码
(4)这里须要说明下关于 React依赖注入说明(mapStateToProps/mapDispatchToProps

将须要的state的节点注入到与此视图数据相关的组件上node

function mapStateToProps(state, ownProps) {
    return {
            loading:state.getIn(['projectPre', 'projectMgr', 'loading']),
            data:state.getIn(['APP', 'data']),
      ...
    }
    // loading、data都是来自对应的reduce
}
复制代码

将须要绑定的响应事件注入到组件上react

function mapDispatchToProps(dispatch){
    return {
        ...bindActionCreators(action, dispatch)
    }
}
// mapDispatchToProps()函数的bindActionCreators、action须要引入
    // import * as action from '../action';
    // import { bindActionCreators } from 'redux';
------------------------------------
------------------------------------
// 多个action 引入
import * as action from '../action';
import * as action2 from '../../../inde/action';
function mapDispatchToProps(dispatch){
    return {
        ...bindActionCreators(action, dispatch)
        ...bindActionCreators(action2, dispatch)
    }
}

------------------------------------
------------------------------------
// 引入一个action里面的多个方法
import { activeOrAbandonedApproval, showSeller, getAddOrderDiscounts } from '../orderInfo/action'
function mapDispatchToProps(dispatch) {
  return {
    ...bindActionCreators({ activeOrAbandonedApproval, showSeller, getAddOrderDiscounts }, dispatch)
  }
}
复制代码
(5)经过+发送 action,经过 reducer 改变相应的 state
export default {
  ...
  reducers: {
    add1(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 };
    },
  },
};
复制代码
(6)首先咱们在 models/example.js,写相应的 reducer
export default {
  namespace: "count",

  state: {
    record: 0,
    current: 0
  },

  subscriptions: {
    setup({ dispatch, history }) {
      // eslint-disable-lines
    }
  },

  effects: {
    *fetch({ payload }, { call, put }) {
      // eslint-disable-line
      yield put({ type: "save" });
    }
  },

  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 };
    }
  }
};
复制代码
(7)在页面的模板 routes/IndexPage.js+ 号点击的时候,dispatch 一个 action
import React from "react";
import { connect } from "dva";
import styles from "./IndexPage.css";

class IndexPage extends React.Component {
  componentDidMount() {
    console.log(this.props);
  }
  render() {
    const { dispatch, count } = this.props;

    return (
      <div className={styles.normal}>
        <div className={styles.record}>Highest Record: {count.record}</div>
        {/* // 将count的record输出 */}
        <div className={styles.current}>{count.current}</div>
        <div className={styles.button}>
          <button
            onClick={() => {
              dispatch({ type: "count/add" });
            }}
          >
            +
          </button>
        </div>
      </div>
    );
  }
}

function mapStateToProps(state) {
  return { count: state.count };
} // 获取state

export default connect(mapStateToProps)(IndexPage);
复制代码
效果以下:

5.接下来咱们来使用 effect 模拟一个数据接口请求,返回以后,经过 yield put() 改变相应的state

(1)首先咱们替换相应的 models/example.jseffect
effects: {
    *add(action, { call, put }) {
      yield call(delay, 1000);
      yield put({ type: 'minus' });
    },
},
复制代码
(2)这里的 delay,写的一个延时的函数,咱们在 utils 里面编写一个 utils.js ,通常请求接口的函数都会写在 servers文件夹中。
export function delay(timeout) {
  return new Promise((resolve) => {
    setTimeout(resolve, timeout);
  });
}
复制代码
(3)接着在 models/example.js 导入这个 utils.js
import { delay } from '../utils/utils';
复制代码

6.订阅订阅键盘事件,使用 subscriptions,当用户按住command+up 时候触发添加数字的 action

(1)你须要安装 keymaster 这个依赖
npm install keymaster --save
复制代码
(2)在models/example.js中做以下修改,这里windows中的up就是键盘的.
import key from 'keymaster';
...
app.model({
  namespace: 'count',
+  subscriptions: {
+    keyboardWatcher({ dispatch }) {
+      key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });
+    },
+  },
});
复制代码

7.例子中咱们看到当咱们不断点击+按钮以后,咱们会看到current会不断加一,可是1s事后,他会自动减到零。那么咱们如何修改呢?

(1)咱们应该在effect中发送一个关于添加的action,可是咱们在effect中不能直接这么写
effects: {
    *add(action, { call, put }) {
      yield put({ type: 'add' });
      yield call(delay, 1000);
      yield put({ type: 'minus' });
    },
  },
复制代码
由于若是这样的话,effectreducers中的add方法重合了,这里会陷入一个死循环,由于当组件发送一个dispatch的时候,model会首先去找effect里面的方法,当又找到add的时候,就又会去请求effect里面的方法。
(2)应该更改reducers里面的方法,使它不与effect的方法同样,将reducers中的add改成add1.
reducers: {
    add1(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 put({ type: 'add1' });
      yield call(delay, 1000);
      yield put({ type: 'minus' });
    },
  },
复制代码

结语

关于dva框架的model总结git

1.state

这里的state 跟以前用 state 的概念是同样的,只不过她的优先级比初始化的低,但在项目中的 state 基本都是在这里定义的。github

2.namespace

model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,咱们发送在发送 action 到相应的 reducer 时,就会须要用到namespaceweb

3.Reducer

key/value 格式定义 reducer,用于处理同步操做,惟一能够修改 state 的地方。由 action 触发。其实一个纯函数。npm

4.Effect

用于处理异步操做和业务逻辑,不直接修改 state,简单的来讲,就是获取从服务端获取数据,而且发起一个 action 交给 reducer的地方。json

其中它用到了redux-saga,里面有几个经常使用的函数。redux

*add(action, { call, put }) {
    yield call(delay, 1000);
    yield put({ type: 'minus' });
},
复制代码
5.Effects

(1) put,用于触发actionwindows

yield put({ type: '',payload:'' });
复制代码

(2)call 用于异步逻辑处理,支持Promise

const result= yield call(fetch,'');
复制代码

(3)select 用于state获取数据

const todo = yield select(state=>state.todo);
复制代码
6.Subscription

subscription 是订阅,用于订阅一个数据源,而后根据须要 dispatch 相应的 action。在 app.start() 时被执行,数据源能够是当前的时间、当前页面的url、服务器的 websocket 链接、history路由变化等等。

相关文章
相关标签/搜索