Riot vs React & Polymer
Как Riot отличается от похожих проектов.
React
React и идея “связанности” послужили фундаментом для Riot. Согласно разработчикам Facebook:
“Templates separate technologies, not concerns.”
Шаблоны разделяют технологии, не ответственность. Мы придерживаемся этого принципа. Мы стремимся прийти к созданию повторно используемых компонентов, а не шаблонов. Разделяя логику наших интерфейсов и их шаблоны, мы, на самом деле, лишь усложняем себе жизнь.
Объединяя шаблон с его логикой в одном компоненте, мы делаем всю систему чище. Спасибо React за эту идею!
React отлично работает, и мы по-прежнему используем его в некоторых наших проектах. Но мы были обеспокоены размером React и его синтаксисом (особенно синтаксисом). Мы стали задумываться о том, что он может быть прощё; не только внутренне, но и для конечного пользователя.
React синтаксис
Этот пример взят с домашней страницы React:
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.render(<TodoApp />, mountNode);
JSX смешивает HTML и JavaScript. Вы можете использовать HTML где угодно внутри компонента; внутри методов и при определении свойств.
Riot синтаксис
Теперь тоже самое с Riot:
<todo>
<h3>TODO</h3>
<ul>
<li each={ item, i in items }>{ item }</li>
</ul>
<form onsubmit={ handleSubmit }>
<input>
<button>Add #{ items.length + 1 }</button>
</form>
this.items = []
handleSubmit(e) {
var input = e.target[0]
this.items.push(input.value)
input.value = ''
}
</todo>
Этот пользовательский тег монтируется в страницу следующим образом:
<todo></todo>
<script>riot.mount('todo')</script>
Похоже, но не одно и то же!
В Riot HTML и JavaScript используются гораздо более привычным способом. Оба находятся в одном компоненте, но аккуратно отделены друг от друга. При этом, HTML можно смешивать с выражениями JavaScript.
Мы не используем собственный язык, за исключением выражений в фигурных скобках.
Вам приходится работать с меньшим объёмом кода. Меньше скобок, запятых, системных методов и свойств. Строки можно исползовать как "Hello {world}"
вместо "Hello " + this.state.world
, и методы могут быть определены с помощью компактного ES6 синтаксиса. Во всем - меньше.
Мы считаем, что синтаксис Riot - наиболее чистый способ отделения шаблона и его логики, который позволяет пользоваться всеми преимуществами изолированных, пригодных для повторного использования компонентов.
Обращения к DOM vs парсинг строк
Когда компонент инициализирован, React парсит строку, а Riot перебирает DOM-дерево.
Riot берёт выражения из DOM и сохраняет их в массив. Каждое выражение имеет указатель на на элемент DOM. При каждом выполнении этого выражения, оно сравнивается со значение в DOM. Когда значение меняется, элемент DOM обновляется. Планируется, что у Riot будет свой собственный виртуальный DOM, как в React, но гораздо проще.
Так как выражение может быть закэшировано, цикл обновления очень быстр. Проход 100 или 1000 выражений обычно занимает 1мс и меньше.
Алгоритм React гораздо сложнее из-за того, что HTML может непредвиденно изменяться после каждого обновления. Получив эту проблему, разработчики Facebook проделали впечатляющую работу с ней.
Мы увидели, что сложность, связанную с определением различий можно избежать.
В Riot HTML структура фиксированная. Только в переборах могут появляться и удаляться элементы. Но div
не может стать label
, к примеру. Riot обновляет выражения без сложных замен поддеревьев.
Flux и маршрутизация
React работает только с UI, и это - хорошо. Все хорошие проекты имеют конкретный фокус.
Facebook рекомендует использовать Flux для структурирования кода на стороне клиента. Это скорее паттерн проектирования, сочетающий хорошие идеи, чем фреймворк.
Riot предоставляется в комплекте с пользовательскими тегами, событийной системой (observable) и маршрутизатором. Мы считаем это минимальным набором для создания приложения на клиенте. События привносят модульность, маршрутизатор заботится об URL и кнопке “назад”, а пользовательские теги берут на себя UI.
Так же, как Flux, Riot является очень гибок и оставляет основные архитектурные решения для разработчиком. Это просто библиотека, которая помогает вам достичь цели.
Вы можете создать Flux-подобную систему, используя observable и router, встроенные в Riot. Вообще-то, такие инструменты уже есть.
x bigger
React (v16.4.0) в раз больше Riot.
react.min.js – 33.27KB
riot.min.js – 10.85KB
Маршрутизатор, рекомендованный React (v4.1.1) в раз больше, чем в Riot.
react-router.min.js – 10.95KB
react-mini-router.min.js – 4.52KB
riot.route.min.js – 1.77KB
Правда, это сравнение немного несправедливо, ведь у react-router гораздо больше возможностей. Но представленные выше графики прекрасно иллюстрируют главную цель Riot: предоставить наиболее минималистичный API для работы.
Экосистема React более “фреймворковая” и это провоцирует громоздкие API. Компактная альтернатива react-mini-router не пользуется популярностью в сообществе React.
Polymer
Polymer взял и стандартные Wev компоненты и сделал их доступными для современных браузеров. Это позволяет вам создавать пользовательские теги в стандартной манере.
В принципе, Riot делает тоже самое, но иначе.
-
Riot обновляет только те элементы, у которых есть изменения, чтобы сократить манипуляции с DOM.
-
Polymer синтаксис более сложный и требует изучения многих книг.
-
Отдельные компоненты импортируются через HTML
link rel="import"
. Приходится прибегать к последовательным XHR-запросам, что делает Polymer крайне медленным, если не использовать vulcanize. Пользовательские теги в Riot импортируются черезscript src
и множество тегов могут быть объединены тем же способом, что и обычные js-файлы. -
Нет возможности рендеринга на стороне сервера.
В раз больше
Polymer(v1.8.0) + WebComponents(v0.7.24) в раз больше, чем Riot
polymer.min.js – 49.38KB
riot.min.js – 10.85KB
WeВеб-компоненты считаются основой всех проблем polyfilling. Это главная причина, по которой Polymer нуждается в таком количестве кода.
Экспериментальный
Polymer основан на экспериментальной технологии. Поддержка нативных web-компонентов не представлена в Safari или IE. В IE они в статусе “на рассмотрении”, в Safari - пока не известно. В некоторых коммитах в WebKit отмечается, что они не планируют поддерживать стандартные web-компоненты. И Polymer предоставляет полифилы только для последних версий браузеров (IE 10+).
Polymer существует уже больше двух лет и не получил за это время существенного признания. Неизвестно, будут ли когда-либо поддерживаться нативные Веб-компоненты.