什么使无功系统好?

这篇文章是有关自动跟踪(Ember.js中的新反应系统)的系列文章中的第二篇。我还将讨论一般的反应性概念,以及它如何在JavaScript中体现出来。


译者:Chris Garrett-为LinkedIn工作,是Ember js框架的核心贡献者之一。他积极参与了该框架的新版本Ember Octane的开发该版本的基石之一是基于自动跟踪(autotracking)的新型反应系统。尽管他的系列是为Ember开发人员编写的,但它涉及了对所有Web程序员都有用的概念。


  1. 什么是反应性?
  2. 什么使无功系统好?←这篇文章
  3. 自动跟踪的工作原理
  4. 自动跟踪案例-TrackedMap
  5. 自动跟踪案例-@localCopy
  6. 自动跟踪案例-RemoteData
  7. 自动跟踪案例-效果()

, . , :


: , .

, , . : ?


, , . . , , . , , .


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


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. 读取系统中的状态会导致无功导数状态
  3. 默认情况下,系统将不必要的工作减至最少。
  4. 系统防止派生状态冲突

我并不是说这份清单是详尽无遗的,但是它涵盖了使无功系统可靠和可用的许多内容。在下一篇文章中,我们将深入研究自动跟踪,并了解它如何实现这些原则。


All Articles