¿Qué hace que un sistema reactivo sea bueno?

Esta publicación es la segunda de una serie de artículos sobre seguimiento automático : el nuevo sistema de reactividad en Ember.js. También discuto el concepto de reactividad en general, y cómo se manifiesta en JavaScript.


De un traductor: Chris Garrett: trabaja para LinkedIn y es uno de los principales contribuyentes al marco Ember js. Participó activamente en la creación de la nueva edición del marco: Ember Octane . Una de las piedras angulares de esta edición es el nuevo sistema de reactividad basado en el seguimiento automático (seguimiento automático). A pesar de que su serie fue escrita para desarrolladores de Ember, toca conceptos que son útiles para todos los programadores web.


  1. ¿Qué es la reactividad?
  2. ¿Qué hace que un sistema reactivo sea bueno? ← Esta publicación
  3. Cómo funciona el seguimiento automático
  4. Caso de seguimiento automático - TrackedMap
  5. Estuche para seguimiento automático - @localCopy
  6. Caso de seguimiento automático - RemoteData
  7. Estuche para seguimiento automático - efecto ()

, . , :


: , .

, , . : ?


, , . . , , . , , .


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


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. Leer un estado en un sistema conduce a un estado derivado reactivo
  3. El sistema minimiza el trabajo innecesario por defecto.
  4. El sistema evita el estado derivado en conflicto

No pretendo que esta lista sea exhaustiva, pero cubre gran parte de lo que hace que los sistemas reactivos sean confiables y utilizables. En la próxima publicación, profundizaremos en el seguimiento automático y descubriremos cómo implementa estos principios.


All Articles