Простое управление и демо о фреймворке dva

Командная строка редкоземельный

О два

Потому что раньше я использовал react+redux. На этот раз я связался с фреймворком два для простого исследования. В этой статье в основном объясняется разработка общих API-интерфейсов и некоторые навыки использования в среде dva.Если вы хотите просмотреть более подробные API-интерфейсы, обратитесь к официальной документации dva:Официальная демоверсия

Простые шаги установки

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 // 当前整一个项目的依赖

Простая демонстрация, ссылкаОфициальная демонстрация расчета

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, Откройте аннотацию следующей строки модели.
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имя измененоcount,stateобъект плюсrecordа такжеcurrentАтрибуты
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узел вводится в компонент, связанный с данными этого представления

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

Внедрить события ответа, которые необходимо привязать к компоненту.

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.jsизeffect
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) Вам необходимо установить этот мастер ключей зависимостей
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Оно продолжит увеличиваться на единицу, но через 1 с автоматически уменьшится до нуля. Итак, как мы его модифицируем?

(1) мы должныeffectОтправьте сообщение о добавленииaction, но мыeffectНе могу написать так прямо в
effects: {
    *add(action, { call, put }) {
      yield put({ type: 'add' });
      yield call(delay, 1000);
      yield put({ type: 'minus' });
    },
  },
Потому что если так,effectа такжеreducersсередина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Суммировать

1.state

здесьstateиспользовать доstateКонцепция та же, только приоритет у нее ниже, чем у инициализации, но в проектеstateВ основном это определено здесь.

2.namespace

modelпространство имен, но и его глобальноеstateВ свойстве можно использовать только строки, которые мы отправляем в поле sendactionсоответствующемуreducer, вам нужно будет использоватьnamespace.

3.Reducer

кkey/valueопределение форматаreducerДля обработки синхронной операции единственная модификацияstateМесто. Зависит отactionвызывать. На самом деле чистая функция.

4.Effect

Используется для обработки асинхронных операций и бизнес-логики без прямого изменения.stateпроще говоря, заключается в получении данных с сервера и инициированииactionсдаватьreducerМесто.

который он использовалredux-saga, который содержит несколько часто используемых функций.

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

(1) put, используется для запуска действия

yield put({ type: '',payload:'' });

(2)callИспользуется для асинхронной логической обработки, поддерживает Promise

const result= yield call(fetch,'');

(3)selectИспользуется для получения данных из состояния

const todo = yield select(state=>state.todo);
6.Subscription

subscriptionэто подписка, используемая для подписки на источник данных, а затем по мере необходимостиdispatchсоответствующийactioн. существуетapp.start()выполняется, когда источником данных может быть текущее время, текущая страницаurl, серверwebsocketсоединять,historyИзменение маршрута и т.д.