O que torna um sistema reativo bom?

Este post é o segundo de uma série de artigos sobre rastreamento automático - o novo sistema de reatividade do Ember.js. Também discuto o conceito de reatividade em geral e como ele se manifesta em JavaScript.


De um tradutor: Chris Garrett - trabalha para o LinkedIn e é um dos principais colaboradores da estrutura do Ember js. Ele participou ativamente da criação da nova edição do framework - Ember Octane . Um dos pilares desta edição é o novo sistema de reatividade baseado no rastreamento automático (rastreamento automático). Apesar de sua série ter sido escrita para desenvolvedores de Ember, ela aborda conceitos úteis para todos os programadores da Web.


  1. O que é reatividade?
  2. O que torna um sistema reativo bom? ← Este post
  3. Como o rastreamento automático funciona
  4. Caso de rastreamento automático - TrackedMap
  5. Caso para rastreamento automático - @localCopy
  6. Caso de rastreamento automático - RemoteData
  7. Caso para rastreamento automático - effect ()

, . , :


: , .

, , . : ?


, , . . , , . , , .


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


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. Ler um estado em um sistema leva a um estado derivado reativo
  3. O sistema minimiza o trabalho desnecessário por padrão.
  4. O sistema evita um estado derivado conflitante

Não afirmo que esta lista seja exaustiva, mas cobre muito do que torna os sistemas reativos confiáveis ​​e utilizáveis. No próximo post, vamos nos aprofundar no rastreamento automático e descobrir como ele implementa esses princípios.


All Articles