Cet article est le deuxième d'une série d'articles sur le suivi automatique - le nouveau système de réactivité dans Ember.js. Je discute également du concept de réactivité en général et de la manière dont il se manifeste en JavaScript.
D'un traducteur: Chris Garrett - travaille pour LinkedIn et est l'un des principaux contributeurs au framework Ember js. Il a participé activement à la création de la nouvelle édition du framework - Ember Octane . L'une des pierres angulaires de cette édition est le nouveau système de réactivité basé sur le suivi automatique (autotracking). Malgré le fait que sa série a été écrite pour les développeurs Ember, elle touche à des concepts utiles à tous les programmeurs Web.
- Qu'est-ce que la réactivité?
- Qu'est-ce qui rend un système réactif bon? ← Cet article
- Fonctionnement du suivi automatique
- Cas de suivi automatique - TrackedMap
- Cas pour le suivi automatique - @localCopy
- Cas de suivi automatique - RemoteData
- Cas pour le suivi automatique - effet ()
, . , :
: , .
, , . : ?
, , . . , , . , , .
, , . , , . ! , , - ( ).
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
:
- , , ,
- La lecture d'un état dans un système conduit à un état dérivé réactif
- Le système minimise le travail inutile par défaut
- Le système empêche les états dérivés conflictuels
Je ne prétends pas que cette liste soit exhaustive, mais elle couvre une grande partie de ce qui rend les systèmes réactifs fiables et utilisables. Dans le prochain article, nous explorerons le suivi automatique et découvrirons comment il met en œuvre ces principes.