Win-win с react.js
Максим Климишин
!
CTO at Zakaz.ua
!
!
@maxmaxmaxmax
• Бегаю
• Рублю дрова
• Увлекаюсь политикой
Обо мне
React.js
React.js
JavaScript библиотека для создания UI
(Wikipedia)
!
!
Надо описать, как приложение должно
выглядеть в любой момент времени и
React.js автоматически будет управлять
всеми обновлениями UI, если основные
данные поменяются
Что такое UI?
UI – это интерактивная проекция
состояния приложения
React.js tree
• Компоненты React.js реализованы в
виде дерева
• Есть Root Node, от которой все пляшет
• Каждый компонент может содержать
дочерние компоненты
• В дочерние компоненты можно
передавать состояние в виде аттрибутов
• Дочерний компонент не может изменить
props, которые были ему переданы из
родительского компонента
Виртуальное DOM дерево
• Virtual DOM - это объекты JS, которые
описывают структуру UI
• Реакт использует алгоритм, для
нахождения минимального числа шагов
модификаций DOM дерева в браузере
JSX
• XML-похожий трансформер синтаксиса
в JavaScript (React.DOM)
!
• Визуализирует структуру DOM внутри
React.js компонент
!
• Помогает избежать избыточного кода на
чистом JavaScript и повысить
читаемость компонент
Hello, John!
/** @jsx React.DOM */!
var HelloMessage = React.createClass({!
render: function() {!
return <div>Hello {this.props.name}</div>;!
}!
});!
!
React.renderComponent(!
<HelloMessage name="John" />, mountNode);!
JSX to JS
/** @jsx React.DOM */!
var HelloMessage = React.createClass({displayName:
'HelloMessage',!
render: function() {!
return React.DOM.div(null, "Hello ", this.props.name);!
}!
});!
!
React.renderComponent(!
HelloMessage( {name:"John"} ),
document.getElementById("hello"));!
Rendering
• Во время вызова setState реакт
помечает его как “грязный” (dirty) и
перестраивает виртуальное DOM
дерево
• На следующей итерации event loop-a
происходит обновление
Diff алгоритмы и массивы
• Особый случай для списков - по
умолчанию элементы списка
ассоциировались последовательно
• Для однозначного маппинга между
элементом списка и компонентой
React.js необходимо указывать ключ
Решаем сами, что рендерим
• По желанию можно самостоятельно
решать изменилось ли дерево
boolean shouldComponentUpdate(
object nextProps, object nextState)
Синтетические события
• React.js слушает события только с
корневой ноды
• Когда событие происходит, react.js ищет
компонент, соответствующей ноде,
которая сгенерировала событие
• Дальше React.js оборачивает событие в
SyntheticEvent
• И это дело работает совершенно
одинаково во всех поддерживаемых
браузерах
Работа с CSS
• Поначалу в React.js было неудобно
работать с классами CSS
<div className=“class-a class-b class-c class-d class-e
class-f class-…>
</div>
• Но появился classSet и жизнь
наладилась
classSet
{
render: function() {
var cx = React.addons.classSet;
var classes = cx({
'message': true,
'message-important': this.props.isImportant,
'message-read': this.props.isRead
});
// same final string, but much cleaner
return (
<div className={classes}>Great, I'll be there.</div>
)
}
}
Рутинное телодвижение кажется
сложным
var NoLink = React.createClass({
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function() {
var value = this.state.value;
return <input type="text" value={value}
onChange={this.handleChange} />;
}
});
Two way data binding
• Angular и Ember разбаловал некоторых
разработчиков в плане two-way data
binding.
!
• Но пацанчики из facebook оказались
ровными и сделали linkState …
Two way data binding
var WithLink = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {value: 'Hello!'};
},
render: function() {
return <input type="text"
valueLink={this.linkState('value')} />;
}
});
Чем хорошо для дела
• Практически нет гемора для поддержки
зоопарка браузеров
• Быстрая и “не распухающая” разработка
новых фич
• Легко внедряется в существующий и
работающий код
• Легко ре-использовать компоненты
• Сложно написать “говнокод” - помогает
односторонний data flow
Производительность
Based on: http://vuejs.org/perf/
Backbone
React
Plain JS
0 10 20 30 40
Анимация 100 кружков, Chrome 34, ~ms на цикл
Model & Controller
React.js занимается только
уровнем представления
А что насчет приложений?
Flux
Flux - это архитектура/design pattern
приложений для Rect.js, которая
использует однонаправленный поток
данных
Идея архитектуры Flux
Что внутри Flux?
• По сути Flux - design pattern, уникальная
тулза - Dispatcher
• В экземпляр диспетчера региструются
колебли, присваивается уникальный ID
• Диспетчеру можно послать некоторое
событие (dispatcher.dispatch)
• Внутри коллбека можно объявить
зависимость от выполнения другого
(dispatcher.waitFor)
• Коллбеки будут выполнены согласно
описанным зависимостям
Какую проблемы решает Flux?
• Мультинаправленное движение данных
внутри приложения
• Структурную организацию React.js-
based приложений
• Избыточный инструментарий, которые
приезжает с хранилищами из других
подходов
• Структурирование зависимостей
хэндлеров событий
Bidirectional data flow
Button 1
Button 2
Input 1
Input 2
Result area 1
Result area 2
Handler 3
API Endpoint 1
API Endpoint 2
API Endpoint 3
Handler 2
Handler 1
Handler 4
Среднесложное приложение на jQuery
Bidirectional data flow
Action
View 1
View 2
View 3
State Action
State
State
State
Flux Dispatcher
var base = new Dispatcher();
var v1 = {"country": null}, v2 = {"city": null},
v3 = {"price": null};
!
v1.dispatchToken = base.register(function (payload) {
console.log("v1 handler");
if (payload.actionType == "update-v1")
v1.country = payload.country;
console.log(v1.country, v2.city, v3.price);
});
!
Flux Dispatcher
v2.dispatchToken = base.register(function (payload) {
console.log("v2 handler");
if (payload.actionType === "update-v1") {
base.waitFor([v1.dispatchToken]);
v2.city = payload.country + " - related data to v2";
}
console.log(v1.country, v2.city, v3.price);
});
!
base.register(function (payload) {
console.log("v3 handler");
if (payload.actionType === "update-v2") {
base.waitFor([v1.dispatchToken, v2.dispatchToken]);
v2.city = payload.city; v3.price = 123;
}
console.log(v1.country, v2.city, v3.price);
});
Flux Dispatcher
!
document.getElementById("dispatch").addEventListener("click",
function () {
base.dispatch({
actionType: "update-v1",
country: 'ukraine'
});
base.dispatch({
actionType: "update-v2",
city: "kiev"});
});
Результат
v1 handler
ukraine kiev 123
v2 handler
ukraine ukraine - related data to v2 123
v3 handler
ukraine ukraine - related data to v2 123
!
v1 handler
ukraine ukraine - related data to v2 123
v2 handler
ukraine ukraine - related data to v2 123
v3 handler
ukraine kiev 123
А нахера это нужно?
Разделение ответственности (Separation
of concerns) - уменьшать связанность
между модулями, увеличивать
связность внутри модуля
Please, stop the bullshit
© John Legere, T-Mobile CEO
Coupling vs cohesion?
• Связанность плоха тем, что она мешает
менять компоненты независимо от
общего кода
• Разработчику необходимо понимать как
работает вся система для того, чтобы
безопасно внести изменения
• Изменения требований потенциально
влечет рефакторинг нескольких
компонент
• Стоимость изменений выше
Спасибо за горячую воду, Львов!
Спасибо!
!
facebook.github.io/react/
!
facebook.github.io/flux/

LvivJS 2014 - Win-win c React.js

  • 1.
    Win-win с react.js МаксимКлимишин ! CTO at Zakaz.ua ! ! @maxmaxmaxmax
  • 2.
    • Бегаю • Рублюдрова • Увлекаюсь политикой Обо мне
  • 3.
  • 4.
    React.js JavaScript библиотека длясоздания UI (Wikipedia) ! ! Надо описать, как приложение должно выглядеть в любой момент времени и React.js автоматически будет управлять всеми обновлениями UI, если основные данные поменяются
  • 5.
    Что такое UI? UI– это интерактивная проекция состояния приложения
  • 6.
    React.js tree • КомпонентыReact.js реализованы в виде дерева • Есть Root Node, от которой все пляшет • Каждый компонент может содержать дочерние компоненты • В дочерние компоненты можно передавать состояние в виде аттрибутов • Дочерний компонент не может изменить props, которые были ему переданы из родительского компонента
  • 7.
    Виртуальное DOM дерево •Virtual DOM - это объекты JS, которые описывают структуру UI • Реакт использует алгоритм, для нахождения минимального числа шагов модификаций DOM дерева в браузере
  • 8.
    JSX • XML-похожий трансформерсинтаксиса в JavaScript (React.DOM) ! • Визуализирует структуру DOM внутри React.js компонент ! • Помогает избежать избыточного кода на чистом JavaScript и повысить читаемость компонент
  • 9.
    Hello, John! /** @jsxReact.DOM */! var HelloMessage = React.createClass({! render: function() {! return <div>Hello {this.props.name}</div>;! }! });! ! React.renderComponent(! <HelloMessage name="John" />, mountNode);!
  • 10.
    JSX to JS /**@jsx React.DOM */! var HelloMessage = React.createClass({displayName: 'HelloMessage',! render: function() {! return React.DOM.div(null, "Hello ", this.props.name);! }! });! ! React.renderComponent(! HelloMessage( {name:"John"} ), document.getElementById("hello"));!
  • 12.
    Rendering • Во времявызова setState реакт помечает его как “грязный” (dirty) и перестраивает виртуальное DOM дерево • На следующей итерации event loop-a происходит обновление
  • 13.
    Diff алгоритмы имассивы • Особый случай для списков - по умолчанию элементы списка ассоциировались последовательно • Для однозначного маппинга между элементом списка и компонентой React.js необходимо указывать ключ
  • 14.
    Решаем сами, чторендерим • По желанию можно самостоятельно решать изменилось ли дерево boolean shouldComponentUpdate( object nextProps, object nextState)
  • 15.
    Синтетические события • React.jsслушает события только с корневой ноды • Когда событие происходит, react.js ищет компонент, соответствующей ноде, которая сгенерировала событие • Дальше React.js оборачивает событие в SyntheticEvent • И это дело работает совершенно одинаково во всех поддерживаемых браузерах
  • 16.
    Работа с CSS •Поначалу в React.js было неудобно работать с классами CSS <div className=“class-a class-b class-c class-d class-e class-f class-…> </div> • Но появился classSet и жизнь наладилась
  • 17.
    classSet { render: function() { varcx = React.addons.classSet; var classes = cx({ 'message': true, 'message-important': this.props.isImportant, 'message-read': this.props.isRead }); // same final string, but much cleaner return ( <div className={classes}>Great, I'll be there.</div> ) } }
  • 18.
    Рутинное телодвижение кажется сложным varNoLink = React.createClass({ getInitialState: function() { return {value: 'Hello!'}; }, handleChange: function(event) { this.setState({value: event.target.value}); }, render: function() { var value = this.state.value; return <input type="text" value={value} onChange={this.handleChange} />; } });
  • 19.
    Two way databinding • Angular и Ember разбаловал некоторых разработчиков в плане two-way data binding. ! • Но пацанчики из facebook оказались ровными и сделали linkState …
  • 20.
    Two way databinding var WithLink = React.createClass({ mixins: [React.addons.LinkedStateMixin], getInitialState: function() { return {value: 'Hello!'}; }, render: function() { return <input type="text" valueLink={this.linkState('value')} />; } });
  • 21.
    Чем хорошо длядела • Практически нет гемора для поддержки зоопарка браузеров • Быстрая и “не распухающая” разработка новых фич • Легко внедряется в существующий и работающий код • Легко ре-использовать компоненты • Сложно написать “говнокод” - помогает односторонний data flow
  • 22.
    Производительность Based on: http://vuejs.org/perf/ Backbone React PlainJS 0 10 20 30 40 Анимация 100 кружков, Chrome 34, ~ms на цикл
  • 23.
    Model & Controller React.jsзанимается только уровнем представления
  • 25.
    А что насчетприложений?
  • 26.
  • 27.
    Flux - этоархитектура/design pattern приложений для Rect.js, которая использует однонаправленный поток данных
  • 28.
  • 29.
    Что внутри Flux? •По сути Flux - design pattern, уникальная тулза - Dispatcher • В экземпляр диспетчера региструются колебли, присваивается уникальный ID • Диспетчеру можно послать некоторое событие (dispatcher.dispatch) • Внутри коллбека можно объявить зависимость от выполнения другого (dispatcher.waitFor) • Коллбеки будут выполнены согласно описанным зависимостям
  • 30.
    Какую проблемы решаетFlux? • Мультинаправленное движение данных внутри приложения • Структурную организацию React.js- based приложений • Избыточный инструментарий, которые приезжает с хранилищами из других подходов • Структурирование зависимостей хэндлеров событий
  • 31.
    Bidirectional data flow Button1 Button 2 Input 1 Input 2 Result area 1 Result area 2 Handler 3 API Endpoint 1 API Endpoint 2 API Endpoint 3 Handler 2 Handler 1 Handler 4 Среднесложное приложение на jQuery
  • 32.
    Bidirectional data flow Action View1 View 2 View 3 State Action State State State
  • 33.
    Flux Dispatcher var base= new Dispatcher(); var v1 = {"country": null}, v2 = {"city": null}, v3 = {"price": null}; ! v1.dispatchToken = base.register(function (payload) { console.log("v1 handler"); if (payload.actionType == "update-v1") v1.country = payload.country; console.log(v1.country, v2.city, v3.price); }); !
  • 34.
    Flux Dispatcher v2.dispatchToken =base.register(function (payload) { console.log("v2 handler"); if (payload.actionType === "update-v1") { base.waitFor([v1.dispatchToken]); v2.city = payload.country + " - related data to v2"; } console.log(v1.country, v2.city, v3.price); }); ! base.register(function (payload) { console.log("v3 handler"); if (payload.actionType === "update-v2") { base.waitFor([v1.dispatchToken, v2.dispatchToken]); v2.city = payload.city; v3.price = 123; } console.log(v1.country, v2.city, v3.price); });
  • 35.
    Flux Dispatcher ! document.getElementById("dispatch").addEventListener("click", function (){ base.dispatch({ actionType: "update-v1", country: 'ukraine' }); base.dispatch({ actionType: "update-v2", city: "kiev"}); });
  • 36.
    Результат v1 handler ukraine kiev123 v2 handler ukraine ukraine - related data to v2 123 v3 handler ukraine ukraine - related data to v2 123 ! v1 handler ukraine ukraine - related data to v2 123 v2 handler ukraine ukraine - related data to v2 123 v3 handler ukraine kiev 123
  • 37.
    А нахера этонужно? Разделение ответственности (Separation of concerns) - уменьшать связанность между модулями, увеличивать связность внутри модуля Please, stop the bullshit © John Legere, T-Mobile CEO
  • 38.
    Coupling vs cohesion? •Связанность плоха тем, что она мешает менять компоненты независимо от общего кода • Разработчику необходимо понимать как работает вся система для того, чтобы безопасно внести изменения • Изменения требований потенциально влечет рефакторинг нескольких компонент • Стоимость изменений выше
  • 39.
  • 40.