Was macht ein reaktives System gut?

Dieser Beitrag ist der zweite in einer Reihe von Artikeln zum Thema Auto-Tracking - dem neuen Reaktivitätssystem in Ember.js. Ich diskutiere auch das Konzept der Reaktivität im Allgemeinen und wie es sich in JavaScript manifestiert.


Von einem Übersetzer: Chris Garrett - arbeitet für LinkedIn und ist einer der Hauptverantwortlichen für das Ember js-Framework. Er war aktiv an der Erstellung der neuen Ausgabe des Frameworks beteiligt - Ember Octane . Einer der Eckpfeiler dieser Ausgabe ist das neue Reaktivitätssystem, das auf automatischem Tracking (Autotracking) basiert. Trotz der Tatsache, dass seine Serie für Ember-Entwickler geschrieben wurde, berührt sie Konzepte, die für alle Webprogrammierer nützlich sind.


  1. Was ist Reaktivität?
  2. Was macht ein reaktives System gut? ← Dieser Beitrag
  3. So funktioniert die automatische Verfolgung
  4. Auto Tracking Case - TrackedMap
  5. Fall für die automatische Verfolgung - @localCopy
  6. Auto Tracking Case - RemoteData
  7. Fall für automatische Verfolgung - Effekt ()

, . , :


: , .

, , . : ?


, , . . , , . , , .


, , . , , . ! , , - ( ).


HTML


HTML . , , . HTML ( CSS) , - JavaScript!


-, HTML ? ? HTML :


<form action="/my-handling-form-page" method="post">
  <label>
    Email:
    <input type="email" />
  </label>

  <label>
    Password:
    <input type="password" />
  </label>

  <button type="submit">Log in</button>
</form>

. . — , , - . , , .


​​ : , , , . HTML , , , — (JavaScript). , HTML ? , HTML ?


HTML, input select. , , , . , , .


<style>
  input[type='checkbox'] + ul {
    display: none;
  }

  input[type='checkbox']:checked + ul {
    display: inherit;
  }
</style>

<nav>
  <ul>
    <li>
      <label for="dropdown">Dropdown</label>
      <input id="dropdown" type="checkbox" />
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </li>
  </ul>
</nav>

« , CSS». . ./index.html - HTML / CSS .


HTML (, checked ). HTML- , . , , :


1. , , ,

, , HTML- . - , 10 , , .


. - . , JS .


push


push. , , . , JavaScript, .


. , , , , , - . , - <edit-word> :


customElements.define('edit-word',
  class extends HTMLElement {
    constructor() {
      super();

      const shadowRoot = this.attachShadow({mode: 'open'});
      this.form = document.createElement('form');
      this.input = document.createElement('input');
      this.span = document.createElement('span');

      shadowRoot.appendChild(this.form);
      shadowRoot.appendChild(this.span);

      this.isEditing = false;
      this.input.value = this.textContent;

      this.form.appendChild(this.input);

      this.addEventListener('click', () => {
        this.isEditing = true;
        this.updateDisplay();
      });

      this.form.addEventListener('submit', e => {
        this.isEditing = false;
        this.updateDisplay();
        e.preventDefault();
      });

      this.input.addEventListener('blur', () => {
        this.isEditing = false;
        this.updateDisplay();
      });

      this.updateDisplay()
    }

    updateDisplay() {
      if (this.isEditing) {
        this.span.style.display = 'none';
        this.form.style.display = 'inline-block';
        this.input.focus();
        this.input.setSelectionRange(0, this.input.value.length)
      } else {
        this.span.style.display = 'inline-block';
        this.form.style.display = 'none';
        this.span.textContent = this.input.value;
        this.input.style.width = this.span.clientWidth + 'px';
      }
    }
  }
);

- , . isEditing, updateDisplay span form . , . , updateDisplay .


, , isEditing . , . :


2.

isEditing , . , , — .


, , .


Ember


Ember Classic push. (observers) (evert listeners) , , , . , (binding), (dependency chaining), .


fullName:


import { computed, set } from '@ember/object';

class Person {
  firstName = 'Liz';
  lastName = 'Hewell';

  @computed('firstName', 'lastName')
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

let liz = new Person();

console.log(liz.fullName); 'Liz Hewell';

set(liz, 'firstName', 'Elizabeth');

console.log(liz.fullName); 'Elizabeth Hewell';

Classic Ember . , , , Ember . , set(), .



, (observers) , (computed) (templates) . , , , . ( ) .


Ember , , . . , - . , , . - () — , , .


, , , . , , . , , .


Observables, Streams Rx.js


push, , — Observable. JavaScript RxJS Angular .


, . , , , .


//  JS
let count = 0;
document.addEventListener(
  'click',
  () => console.log(`Clicked ${++count} times`)
);

//   (Streams)
import { fromEvent } from 'rxjs';
import { scan } from 'rxjs/operators';

fromEvent(document, 'click')
  .pipe(scan(count => count + 1, 0))
  .subscribe(count => console.log(`Clicked ${count} times`));

Ember, — , . , , .


, . , , , . , .



, (debounce), , . :


3.

, . , , . , , , , .


, , push, . (lazy) , , , Ember Classic, . , , push, , .


, , . pull.


pull


, , pull, — . , , . , , , - , . , , , .


, , pull. , , . , .


, pull , . , «Virtual DOM».


React VirtualDOM


Virtual DOM, , React.js . , HTML . , , , HTML, React , , HTML.


HTML-. , . -.



, React, , - . , setState API ( useState ).


class Toggle extends React.Component {
  state = { isToggleOn: true };

  handleClick = () => {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

, ( ) .



, (consistency), , setState useState . , ( ). - React , :


4.

React , , . , React :


class Example extends React.Component {
  state = {
    value: 123;
  };

  render() {
    let part1 = <div>{this.state.value}</div>

    this.setState({ value: 456 });

    let part2 = <div>{this.state.value}</div>

    return (
      <div>
        {part1}
        {part2}
      </div>
    );
  }
}

, , part1 , part2 . , , - , . , , . React , .


, React , . API, shouldComponentUpdate() useMemo(), React .


API , ​​ . , .


Vue:


Vue Virtual DOM, . Vue data :


const vm = new Vue({
  data: {
    a: 1
  }
});

Vue setState useState ( , API), . data , , . observables.


, :


const vm = new Vue({
  el: '#example',

  data: {
    message: 'Hello'
  },

  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  }
})

reversedMessage message , message .


Vue , React, . , (memoization) , - , . push, , .


Elm


, , JavaScript. , , (autotracking) , , - .


Elm — , . , ( HTML + JS). , - .


, Elm - , . , Elm .



- Elm , (memoization). , . / , .


//     JS
let lastArgs;
let lastResult;

function memoizedRender(...args) {
  if (deepEqual(lastArgs, args)) {
    // Args
    return lastResult;
  }

  lastResult = render(...args);
  lastArgs = args;

  return lastResult;
}

«» , , Elm .


. , HTML , React / Vue / Virtual DOM.


, , , . Elm, , - .


, Elm JavaScript . JavaScript, , . , . Redux — , , .


, , — , , . , .


!



, , :


  • HTML / CSS
  • push
  • JavaScript
    — Ember
    — Observables / Rx.js
  • pull
    — React.js
    — Vue.js
    — Elm

:


  1. , , ,
  2. Das Lesen eines Zustands in einem System führt zu einem reaktiven Ableitungszustand
  3. Das System minimiert standardmäßig unnötige Arbeit.
  4. System verhindert widersprüchliche abgeleitete Zustände

Ich behaupte nicht, dass diese Liste vollständig ist, aber sie deckt vieles ab, was reaktive Systeme zuverlässig und nutzbar macht. Im nächsten Beitrag werden wir uns mit der automatischen Verfolgung befassen und herausfinden, wie diese Prinzipien implementiert werden.


All Articles