Dieser Beitrag ist der zweite in einer Reihe von Artikeln zum Thema Auto-Tracking - dem neuen Reaktivitätssystem in Ember.js. Ich diskutiere auch das Konzept der Reaktivität im Allgemeinen und wie es sich in JavaScript manifestiert.
Von einem Übersetzer: Chris Garrett - arbeitet für LinkedIn und ist einer der Hauptverantwortlichen für das Ember js-Framework. Er war aktiv an der Erstellung der neuen Ausgabe des Frameworks beteiligt - Ember Octane . Einer der Eckpfeiler dieser Ausgabe ist das neue Reaktivitätssystem, das auf automatischem Tracking (Autotracking) basiert. Trotz der Tatsache, dass seine Serie für Ember-Entwickler geschrieben wurde, berührt sie Konzepte, die für alle Webprogrammierer nützlich sind.
- Was ist Reaktivität?
- Was macht ein reaktives System gut? ← Dieser Beitrag
- So funktioniert die automatische Verfolgung
- Auto Tracking Case - TrackedMap
- Fall für die automatische Verfolgung - @localCopy
- Auto Tracking Case - RemoteData
- Fall für automatische Verfolgung - Effekt ()
, . , :
: , .
, , . : ?
, , . . , , . , , .
, , . , , . ! , , - ( ).
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
:
- , , ,
- Das Lesen eines Zustands in einem System führt zu einem reaktiven Ableitungszustand
- Das System minimiert standardmäßig unnötige Arbeit.
- System verhindert widersprüchliche abgeleitete Zustände
Ich behaupte nicht, dass diese Liste vollständig ist, aber sie deckt vieles ab, was reaktive Systeme zuverlässig und nutzbar macht. Im nächsten Beitrag werden wir uns mit der automatischen Verfolgung befassen und herausfinden, wie diese Prinzipien implementiert werden.