Написание объектно-ориентированных бизнес-моделей с помощью Mota в проектах React

внешний интерфейс JavaScript React.js NPM

Кратко

React — это структура пользовательского интерфейса «уровня просмотра». В обычном MVC React — это просто представление. Когда мы пишем приложения, нам обычно нужно обращать внимание на более важные модели. Для React нам часто нужна «библиотека управления состоянием». Однако в настоящее время большинство библиотек управления состоянием для React являются «сильными зависимостями», которые вторгаются в бизнес-модель, которая должна быть независимой, поэтому код, соответствующий «бизнес-логике», нельзя легко повторно использовать в другом месте. ", но "бизнес-модель" не должна иметь слишком много зависимостей, она не должна зависеть от фреймворка и должна быть готова к использованию в любой подходящей среде JavaScript. Используя mota, вы можете использовать нативный обычный код JavaScript Напишите свой "бизнес модель» и упростить повторное использование вашей «бизнес-модели» в разных средах и средах выполнения.

mota — это «объектно-ориентированная» вспомогательная библиотека приложения React, которая поддерживает «двустороннюю привязку». На основе mota вы можете написать полностью объектно-ориентированную «бизнес-модель» для приложения на чистом JavaScript и легко связать «бизнес-модель». модель" в приложение React.

Связь

Пример

Пример онлайн-списка задач (Пример исходного кода)

Установить

Установить через npm следующим образом

$ npm i mota --save

или поdawnДобавьте шаги, чтобы создать проект, как показано ниже.

$ mkdir your_path
$ cd your_path
$ dn init -t mota
$ dn dev

Вам нужно сначала установить рассвет (Документация по установке и использованию Dawn)

Инженерное сооружение

ОдинmotaОбычная структура проекта выглядит следующим образом.

.
├── README.md
├── package.json
└── src
    ├── assets
    │   ├── common.less
    │   ├── favicon.ico
    │   └── index.html
    ├── components
    │   ├── todoApp.js
    │   └── todoItem.js
    ├── index.js
    └── models
        ├── TodoItem.js
        ├── TodoList.js
        └── index.js

Напишите бизнес-модель

В мота "модель" может быть определенаclassили обычныйObject, весь «уровень бизнес-модели» будет состоять из несколькихclassи несколькоObjectкомпозиции, а знание, необходимое для написания модели, — это знание объектно-ориентированного программирования, присущее JavaScript.

Следующий пример работает, записывая файл с именемUserизclassСоздал «Модель пользователя»

export default class User {
  firstName = 'Jack';
  lastName = 'Hou';
  get fullName(){
    reutrn `${this.firstName} ${this.lastName}`;
  }
}

также может бытьObject, обычно, когда эта модель должна быть «одноэлементной», этот метод можно использовать следующим образом.

export default {
  firstName: 'Jack',
  lastName: 'Hou',
  get fullName(){
    reutrn `${this.firstName} ${this.lastName}`;
  }
};

После того, как "бизнес-модель" написана, можно переходить@modelСвяжите «класс» или «экземпляр класса» с указанным компонентом, а затем его можно будет использовать в компоненте после ассоциации.this.modelПосле доступа к «переменным-членам или методам модели» mota также автоматически «собирает зависимости компонентов». Когда компонент «зависимые данные модели» изменяется, он автоматически реагирует на изменения и «управляет повторным рендерингом компонента», как следует

import { model,binding } from 'mota';
import React from 'react';
import ReactDOM from 'react-dom';
import User from './models/user';

@model(User)
class App extends React.Component {

  onChange(field,event){
    this.model[field] = event.target.value;
  }

  render(){
    return <div>
      <p>{this.model.fullName}</p>
      <p>
        <input onChange={this.onChange.bind(this,'firstName')}/>
        <br/>
        <input onChange={this.onChange.bind(this,'lastName')}/>
      </p>
    </div>;
  }
}

ReactDOM.render(<App/>, mountNode);

Стоит отметить, что при использовании@modelЕсли входящий являетсяclassВ конечном итоге каждый экземпляр компонента будет автоматически создавать один独立的实例, преимущество этого заключается в том, что «когда на странице есть несколько экземпляров одного и того же компонента, они не будут влиять друг на друга».

сопоставление атрибутов

В React приложение обычно делится на несколько компонентов для их повторного использования и передачи ему «свойств» при их использовании Mota предоставляет возможность сопоставлять «свойства компонента» с «данными модели» на основеmodelПрограммирование сделает «уровень просмотра» более единым, сосредоточив внимание на представлении пользовательского интерфейса следующим образом.

@model({ value: 'demo' })
@mapping(['value'])
class Demo extends React.Component {
  render () {
    return <div>{this.model.value}</div>;
  }
}

Код выше проходитmappingБудуDemoэтого компонентаvalueатрибут сопоставляется сmodel.valueвыше, в свойствах компонентаvalueКогда происходит изменение, оно автоматически синхронизируется сmodel.valueсередина.

Сопоставление через карту также может позволить «свойствам компонентов» и «членам модели» использовать разные имена, как показано ниже:

@model({ value: 'demo' })
@mapping({ content: 'value' })
class Demo extends React.Component {
  render () {
    return <div>{this.model.value}</div>;
  }
}

Приведенный выше код, демонстрация компонентаcontentатрибут сопоставляется сmodel.valueначальство.

самовыполняющаяся функция

мота обеспечиваетautorunФункция, которую можно использовать для декорирования методов-членов компонентов React.Декорированный «метод-член» будет автоматически выполняться один раз после монтирования компонента, и mota «соберет данные модели, от которых зависит метод», которые будут использоваться при изменении данных зависимой модели "Автоматически повторно выполнять" соответствующий метод компонента.

Пример

import { Component } from 'react';
import { model, autorun } from 'mota';
import DemoModel from './models/demo';

@model(DemoModel)
export default Demo extends Component {

  @autorun
  test() {
    console.log(this.model.name);
  }

}

В приведенном выше примере кода компонент будет автоматически выполнен после его монтирования.testметод, и mota обнаружит, что метод зависит отmodel.name, затем вmodel.nameКогда происходит изменение, оно будет выполнено повторноtestметод.

Слушайте изменения модели

мота обеспечиваетwatchфункции, которые можно использовать для украшения методов-членов компонентов React,watchВы можете указать "данные модели" для наблюдения. Когда данные модели изменяются, "метод декорированного компонента" будет выполняться автоматически.watchТак же какautorunто же самое, что автоматически один раз, но это то же самое, что иautorunВсе же не то же самое, основные отличия заключаются в следующем

  • autorunбудет автоматически собирать зависимости, иwatchНе заботится о каких-либо зависимостях в методе компонента, вам нужно вручную указать зависимые данные модели
  • watchПо умолчанию «автоматическое выполнение» не выполняется, и для автоматического выполнения в первый раз необходимо явно указать «параметр немедленного выполнения — true».
  • autorunЗависит от самих «данных модели», иwatchКаждый раз зависит от «результата расчета» «функции расчета»

Пример

import { Component } from 'react';
import { model, autorun } from 'mota';
import DemoModel from './models/demo';

@model(DemoModel)
export default Demo extends Component {

  @watch(model=>model.name)
  test() {
    console.log('name 发生了变化');
  }

}

Код выше, черезwatchукрашенtestметод и задает наблюдаемые данные моделиmodel.name, то всякий разmodel.nameКогда есть изменение, он будет печататьname 发生了变化.

watchВыполнять ли повторно, зависит отwatchРезультат вычисления «расчетной функции», передаваемой ей в качестве первого параметра, при каждом изменении данных зависимой модели.watchФункция расчета будет выполнена повторно, при изменении результата расчета будет выполнен декорированный «метод компонента».

export default Demo extends Component {

  @watch(model=>model.name+model.age)
  test() {
    console.log('name 发生变化');
  }

}

Иногда мы желаемwatchможет выполняться автоматически один раз, затем вы можете передатьtrueзаявить об этомwatchВыполняется автоматически один раз.

export default Demo extends Component {

  @watch(model=>model.name,true)
  test() {
    console.log('name 发生变化');
  }

}

вершинаtestметод, будет выполняться автоматически после монтирования компонента, а затем вmodel.nameОн также будет автоматически выполняться повторно, когда происходит изменение.

привязка данных

Основное использование

Не удивляйтесь, это «двусторонняя привязка».motaОправеющиеся «объектно-ориентированные», которые не исключают «двусторонние привязки» и используют MOTA для реализации подобныхngилиvueсвязывающий эффект. Все та же модель в предыдущем разделе, немного изменим код компонента

import { model,binding } from 'mota';
import React from 'react';
import ReactDOM from 'react-dom';
import User from './models/user';

@model(User)
@binding
class App extends React.Component {
  render(){
    const { fullName, firstName, popup } = this.model;
    return <div>
      <p>{fullName}</p>
      <p>
        <input data-bind="firstName"/>
        <button onClick={popup}> click me </button>
      </p>
    </div>;
  }
}
ReactDOM.render(<App/>, mountNode);

"Ключ" это@binding,использовать@bindingПосле этого компонент имеет возможность «двухсторонней привязки».jsxможно назвать поdata-bindобычайattributeграница,data-bindЗначение представляет собой «строку выражения привязки», которую выполняет выражение привязки.scopeдаmodelвместоthis, то есть только с模型的成员связывать.

В одном случае, когда данные для привязки являются переменной цикла, «выражение привязки» будет более проблематичным и немного более длинным для записи, например

@model(userModel)
@binding
class App extends React.Component {
  render(){
    const { userList } = this.model;
    return <ul>
     {userList.map((user,index)=>(
       <li key={user.id}>
         <input type="checkobx" data-bind={`userList[${index}].selected`}>
         {user.name}
       </li>
     ))}
    </ul>;
  }
}

Из-за выполнения «выражения привязки»scopeПо умолчаниюthis.model, а «выражение — это строка», см.userList[${index}].selectedЭто не по-дружески, потому что эта мота также обеспечиваетdata-scopeизattribute, с помощью которого он может изменитьscope, обратитесь к следующему примеру

@model(userModel)
@binding
class App extends React.Component {
  render(){
    const { userList } = this.model;
    return <ul>
     {userList.map(user=>(
       <li key={user.id}>
         <input type="checkobx" data-scope={user} data-bind="selected">
         {user.name}
       </li>
     ))}
    </ul>;
  }
}

пройти черезdata-scopeБудуinputОбъект контекста привязки, объявленный как текущая переменная циклаuser, так что вы можете использоватьdata-bindсвязываться непосредственно с соответствующимuserсвойства включены.

Собственные элементы управления формой

Все собственные элементы управления формы, такие как «обычный ввод, флажок, радио, текстовое поле, выбор», могут быть напрямую привязаны. Среди них «обычный ввод и текстовое поле» относительно просты, просто привяжите данные модели символьного типа к элементу управления, и существует множество различных форм привязки для «флажка и радио».

Привяжите «флажок или радио» кbooleanзначение, в это время проверенный атрибут флажка или радио будет привязан к данным модели, и проверенный ответитbooleanЗначение переменной см. в следующем примере.

@model({ selected:false })
@binding
class App extends React.Component {
  render(){
    return <div>
      <input type="checkbox" data-bind="selected"/>
      <input type="radio" data-bind="selected"/>
    </div>;
  }
}

Передайте пример вышеthis.model.selectedВы можете получить выбранное состояние текущего флажка или радио.

Привяжите флажок к "массиву", обычно несколько флажков привязаны к одной и той же переменной массива. В это время значение флажка привязано к данным, и данные будут содержать значение выбранного в данный момент флажка, как показано ниже.

@model({ selected:[] })
@binding
class App extends React.Component {
  render(){
    return <div>
      <input type="checkbox" data-bind="selected" value="1"/>
      <input type="checkbox" data-bind="selected" value="2"/>
    </div>;
  }
}

Как в примере выше, поthis.selectedВы можете узнать, какие флажки в данный момент выбраны, и получить все выбранные значения.

Я привязываю несколько радиостанций к «переменной типа символа». В это время значение рейдо привязано к данным. Поскольку радио выбрано одиночно, соответствующие данные являются значением текущего выбранного радио, как показано ниже.

@model({ selected:'' })
@binding
class App extends React.Component {
  render(){
    return <div>
      <input type="radio" data-bind="selected" value="1"/>
      <input type="radio" data-bind="selected" value="2"/>
    </div>;
  }
}

пройти черезthis.model.selectedВы можете получить значение текущего выбранного радио

пользовательский компонент

Но для некоторых "компонентов неполной формы" в "библиотеке компонентов" он не может быть напрямую привязан, потому что у mota нет оснований судить о том, что это за компонент. Таким образом, Mota обеспечиваетbindableФункция, которая превращает любой компонент в «привязываемый компонент».

bindable имеет два параметра, используемых для указания «исходных компонентов» и «параметров пакета» соответственно.

//可以这样
const MyComponent = bindable(opts, Component);
//也可这样
const MyCompoent = bindable(Component, opts);

Гуань Цзяньbindableпотребностиopts,пройти черезoptsМы можем сказать mota, как связать этот компонент,optsЕсть два важных члена, его структура выглядит следующим образом

{
  value: ['value 对应的属性名'],
  event: ['value 改变的事件名']
}

Итак, мы можем обернуть пользовательское поле ввода текста, как это

const MyInput = bindable(Input,{
  value: ['value'],
  event: ['onChange']
});

Для этого типа «значение не нужно преобразовывать, изменение может получить значение через событие или event.target.value», упаковка может быть завершена с помощью приведенного выше кода.

за тоonChangeа такжеvalue, потому что значение opts по умолчанию равно

{
  value: ['value'],
  event: ['onChange']
}

Так можно и проще, вот так,

const MyInput = bindable(Input);

Для флажка и радио, как упоминалось выше, он «имеет разные формы привязки в соответствии с разными типами данных», что требует указания функции обработки следующим образом.

const radioOpts = {
  prop: ['checked', (ctx, props) => {
    const mValue = ctx.getValue();
    if (typeof mValue == 'boolean') {
      return !!mValue;
    } else {
      return mValue == props.value;
    }
  }],
  event: ['onChange', (ctx, event) => {
    const { value, checked } = event.target;
    const mValue = ctx.getValue();
    if (typeof mValue == 'boolean') {
      ctx.setValue(checked);
    } else if (checked) ctx.setValue(value);
  }]
};

пройти черезpropВторое значение может указывать «обработчик атрибутов», второе значение относится к событию, которое может принимать обработчик «обработчик событий».ctxэто специальный объект

  • ctx.getValueМожет получить «текущие данные привязанной модели»
  • ctx.setValueМожно установить «текущие связанные данные модели»

вершинаradioКонфигурация, в первую очередь, определяется привязанным «типом данных модели» в «обработчике свойства»checkedЧто является конечным состоянием и возвращается в функции. Опять же, в «обработчике событий» то, какое значение записывается обратно в модель, определяется связанным «типом данных модели».

Почти по «функции обработки свойств» и «Функции обработки событий» могут быть преобразованы в «Привязывание компонентов».

Кроме того, для общегоCheckBoxа такжеRadioКомпоненты типа mota также обеспечивают встроеннуюoptsПоддержка конфигурации, если пользовательский компонент имеет тот же атрибут и модель событий, что и «собственный флажок», его можно просто упаковать следующим образом.

const MyCheckBox = bindable('checkbox',CheckBox);
const MyRadio = bindable('radio',Radio);

Ну вот и все для привязки.

Документация