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.
- O que é reatividade?
- O que torna um sistema reativo bom? ← Este post
- Como o rastreamento automático funciona
- Caso de rastreamento automático - TrackedMap
- Caso para rastreamento automático - @localCopy
- Caso de rastreamento automático - RemoteData
- 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 .
, . , , , .
let count = 0;
document.addEventListener(
'click',
() => console.log(`Clicked ${++count} times`)
);
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). , . / , .
let lastArgs;
let lastResult;
function memoizedRender(...args) {
if (deepEqual(lastArgs, 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
:
- , , ,
- Ler um estado em um sistema leva a um estado derivado reativo
- O sistema minimiza o trabalho desnecessário por padrão.
- 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.