- PVSM.RU - https://www.pvsm.ru -
Автор материала, перевод которого мы сегодня публикуем, полагает [1], что Hyperapp [2] — это заслуживающая внимания альтернатива таким веб-фреймворкам, как React [3] или Vue [4]. Он говорит, что причиной такого утверждения стало то, что он выяснил, что Hyperapp легче в освоении, чем эти два фреймворка. Его идея подверглась критике, так как кажется, что основана она исключительно на его мнении, а такой подход попросту не даёт другим фреймворкам возможности показать их сильные стороны. Эта статья направлена на объективный анализ Hyperapp, React и Vue, проведённый на основе простых примеров, демонстрирующих их возможности, и на основе результатов их испытаний.
Реализация приложения-счётчика, вероятно, является одним из самых часто используемых примеров в реактивном программировании. Он предельно прост и понятен:
count
, которая будет хранить значение счётчика.count
.count
и представления его пользователю.count
.Вот реализация этого примера с использованием рассматриваемых фреймворков.
import React from "react";
import ReactDOM from "react-dom";
Class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0};
}
down(value) {
this.setState(state => ({ count: state.count - value }));
}
up(value) {
this.setState(state => ({ count: state.count + value }));
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick = {() => this.down(1)}>-</button>
<button onClick = {() => this.up(1)}>+</button>
</div>
);
}
}
ReactDOM.render(<Counter />, document.querySelector("#app"));
import Vue from "vue";
new Vue({
data: { count: 0 },
methods: {
down: function(value) {
this.count -= value;
},
up: function(value) {
this.count += value;
}
},
render: function(h) {
return(
<div>
<h1>{this.count}</h1>
<button onClick={() => this.down(1)}>-</button>
<button onClick={() => this.up(1)}>+</button>
</div>
);
},
el: "#app"
});
import { h, app } from "hyperapp";
const state = {
count: 0
};
const actions = {
down: value => state => ({ count: state.count - value}),
up: value => state => ({ count: state.count + value})
};
const view = (state, actions) => (
<div>
<h1>{state.count}</h1>
<button onclick={() => actions.down(1)}>-</button>
<button onclick={() => actions.up(1)}>+</button>
</div>
);
app(state, actions, view, document.querySelector("#app"));
Если вы не знакомы с этими фреймворками, или хотя бы с одним из них, то тут, вероятно, вам встретится кое-что непонятное. Поэтому давайте разберём этот код.
import
.Counter
. Vue идёт похожим путём. Тут создаётся новый экземпляр класса Vue
, ему передаётся информация. И, наконец, в Hyperapp применяется функциональная парадигма, здесь используются самостоятельные сущности view
, state
и actions
.count
, то в React она инициализируется в конструкторе компонента, а в Vue и Hyperapp она представляет собой свойство, соответственно, объектов data
и state
.count
. В React, для изменения состояния приложения, применяется метод setState
, унаследованный от React.Component
. В Vue значение this.count
изменяется напрямую. Методы в Hyperapp написаны с использованием синтаксиса стрелочных функций ES6. Среди рассматриваемых фреймворков он единственный использует подобное, так как React и Vue вынуждены использовать внутри своих методов ключевое слово this
. Методы Hyperapp, с другой стороны, требуют, чтобы им, в качестве аргумента, передавали бы объект с состоянием приложения. Это означает, что их, вероятнее всего, можно будет повторно использовать в различных контекстах.h
. В Hyperapp, вместо onClick
, используется onclick
, а так же здесь обращение к переменной count
осуществляется не так как в React и Vue, что обусловлено особенностями того, как в каждом из фреймворков реализовано хранение состояния приложения.#app
. В каждом из них эта операция выполняется по-разному. Надо отметить, что в Vue эта операция выглядит самой простой и понятной и даёт разработчику более гибкую конструкцию, работая с селектором элемента, а не с самим элементом.Если напрямую сравнить код, решающий одну и ту же задачу, написанный с использованием всех трёх фреймворков, то окажется, что Hyperapp, для реализации приложения-счётчика, требуется меньше всего строк кода, и это — единственный фреймворк, использующий функциональный подход. Однако, объём кода, написанный c использованием Vue, если считать количество символов, получается немного меньше, да и использование в нём селектора элемента выглядит очень хорошо. Код React-приложения кажется самым длинным, но это не означает, что его понять намного сложнее, чем код, написанный для работы с другими анализируемыми фреймворками.
Вполне возможно, что вам, на практике, придётся иметь дело с асинхронным кодом. Одна из самых распространённых асинхронных операций представляет собой отправку запроса некоему API. Для целей этого примера применяется API JSONPlaceholder [6], содержащее условные данные и выдающее список публикаций. Вот что мы собираемся здесь сделать:
posts
) в состоянии приложения.fetch()
, указав нужный нам URL, подождём поступления данных, распарсим полученный JSON-код, представляющий собой массив объектов, и, наконец, обновим переменную posts
, записав в неё полученные данные.posts
с использованием ключей.Рассмотрим код, реализующий вышеописанную схему действий.
import React from "react";
import ReactDOM from "react-dom";
class PostViewer extends React.Component {
constructor(props) {
super(props);
this.state = { posts: [] };
}
getData() {
fetch(`https://jsonplaceholder.typicode.com/posts`)
.then(response => response.json())
.then(json => {
this.setState(state => ({ posts: json}));
});
}
render() {
return (
<div>
<button onClick={() => this.getData()}>Get posts</button>
{this.state.posts.map(post => (
<div key={post.id}>
<h2><font color="#000">{post.title}</font></h2>
<p>{post.body}</p>
</div>
))}
</div>
);
}
}
ReactDOM.render(<PostViewer />, document.querySelector("#app"));
import Vue from "vue";
new Vue({
data: { posts: [] },
methods: {
getData: function(value) {
fetch(`https://jsonplaceholder.typicode.com/posts`)
.then(response => response.json())
.then(json => {
this.posts = json;
});
}
},
render: function(h) {
return (
<div>
<button onClick={() => this.getData()}>Get posts</button>
{this.posts.map(post => (
<div key={post.id}>
<h2><font color="#000">{post.title}</font></h2>
<p>{post.body}</p>
</div>
))}
</div>
);
},
el: "#app"
});
import { h, app } from "hyperapp";
const state = {
posts: []
};
const actions = {
getData: () => (state, actions) => {
fetch(`https://jsonplaceholder.typicode.com/posts`)
.then(response => response.json())
.then(json => {
actions.getDataComplete(json);
});
},
getDataComplete: data => state => ({ posts: data })
};
const view = (state, actions) => (
<div>
<button onclick={() => actions.getData()}>Get posts</button>
{state.posts.map(post => (
<div key={post.id}>
<h2><font color="#000">{post.title}</font></h2>
<p>{post.body}</p>
</div>
))}
</div>
);
app(state, actions, view, document.querySelector("#app"));
Разберём этот код и сравним три исследуемые фреймворка.
fetch()
— операция довольно простая, она работает так, как ожидается, во всех фреймворках. Основное различие здесь, однако, заключается в том, что Hyperapp поддерживает выполнение асинхронных операций немного не так, как другие фреймворки. Вместо того чтобы модифицировать состояние напрямую, внутри асинхронного действия, это действие вызывает другое, синхронное действие, которое получает данные и преобразует их в подходящий формат. Это делает ядро приложения более функциональным и лучше подходящим для разбиения на небольшие части, которые, потенциально, подходят для повторного использования. Такой подход, кроме того, помогает избегать некоторых проблем, свойственных вложенным коллбэкам, которые могут возникнуть в ситуациях, подобных рассматриваемым.Выполнение асинхронных операций оказалось одинаково простым во всех фреймворках. Hyperapp может склонить разработчика к написанию более функционального и модульного кода при работе с асинхронными действиями, но два других фреймворка тоже отлично справляются с поставленной перед ними задачей, и, в этом плане, дают разработчику возможность выбора.
Вероятно, To-Do-приложения — это самый знаменитый пример в области реактивного программирования. По всей видимости, нечто подобное реализовано с использованием почти каждого из существующих фреймворков. Тут мы не будем реализовывать всё приложение. Вместо этого остановимся на простом компоненте без состояния для того, чтобы изучить возможности исследуемых фреймворков по созданию небольших строительных блоков веб-приложений, подходящих для повторного использования.
Рассмотрим реализацию компонента с применением исследуемых фреймворков. В этом примере мы, однако, расширим рассматриваемые варианты кода за счёт рассмотрения React-компонента, написанного в функциональном стиле.
function TodoItem(props) {
return (
<li class={props.done ? "done" : ""} onclick={() => props.toggle(props.id)}>
{props.value}
</li>
);
}
class TodoItem extends React.Component {
render () {
return (
<li class={this.props.done ? "done" : ""} onclick={() => this.props.toggle(this.props.id)}>
{this.props.value}
</li>
);
}
}
var TodoItem = Vue.component("todoitem", {
props: ["id", "value", "done", "toggle"],
template:
'<li v-bind:class="{done : done}" v-on:click="toggle(id)">{{value}}</li>'
});
Обратите внимание на то, что Hyperapp так же использует функциональный стиль.
const TodoItem = ({ id, value, done, toggle }) = (
<li class={done ? "done" : ""} onclick={() => toggle(id)}>
{value}
</li>
);
Для того чтобы привыкнуть к Vue, нужно некоторое время, так как его шаблоны немного отличаются от шаблонов двух других фреймворков. React чрезвычайно гибок, он поддерживает различные подходы, применяемые при создании компонентов. В то же время, в Hyperapp всё устроено очень просто, и он, к тому же, совместим с React, что позволяет, при необходимости, на каком-то этапе проекта, сменить фреймворк.
Ещё одно важное соображение, влияющее на выбор фреймворка, заключается поддерживаемых им событиях жизненного цикла компонентов, на которые можно подписываться и которые можно обрабатывать в соответствии с нуждами разработчика. Вот таблица, созданная на основе анализа API исследуемых систем.
Событие | React | Vue | Hyperapp |
Инициализация | constructor | beforeCreate, created | — |
Монтирование | comoinentDidMount | beforeMount, mounted | oncreate |
Обновление | componentDidUpdate | beforeUpdate, updated | onupdate |
Размонтирование | componentWillUnmount | — | onremove |
Уничтожение | — | beforeDestroy, destroyed | ondestroy |
Вот что можно понять, проанализировав эту таблицу:
В целом можно отметить, что способы обработки событий, возникающих в ходе жизненного цикла компонентов, поддерживают все фреймворки. Эти способы позволят решать множество задач. Все три фреймворка предлагают хуки для всех видов событий, но между ними есть незначительные отличия, источником которых могут служить внутренние особенности фреймворков и различия в их реализации. Пожалуй, Vue в этой области находится на шаг впереди других систем, предлагая более детализированную систему обработки событий, позволяя обрабатывать события жизненного цикла либо до их возникновения, либо после.
Помимо удобства использования фреймворка и применяемых при работе с ним приёмов программирования, многих разработчиков серьёзно заботит и производительность фреймворков, особенно — для достаточно сложных приложений. Ценным источником сведений о производительности различных фреймворков служит проект js-framework-benchmark [7].
Поэтому взглянем на результаты тестирования React, Vue и Hyperapp.
Вот результаты испытаний фреймворков на предмет работы с таблицами. Показатель в ячейках таблицы соответствует длительности выполнения операции ± среднеквадратическое отклонение. В скобках приведён результат деления полученного показателя на самый лучший показатель.
Вот таблица с результатами исследования показателей, имеющих отношение к скорости запуска фреймворка, к его размерам, к использованию им главного потока.
Теперь рассмотрим результаты тестирования выделения памяти.
Если, при разработке некоего проекта, нужно добиться максимальной производительности, вам стоит разобраться, во-первых, с тем, что за приложение вы разрабатываете, а во-вторых — чётко выяснить нужды этого приложения. Если свести воедино анализ производительности всех трёх фреймворков, то возникает такое ощущение, что Vue и React лучше подходят для более сложных приложений, а Hyperapp лучше показывает себя на приложениях меньшего масштаба, в которых нужно обрабатывать меньше данных, которым требуется как можно более быстрый запуск, и которым может понадобиться работать на не самых быстрых компьютерах.
Однако тут стоит помнить, что использованные здесь тесты производительности далеки от реальной жизни, от некоего усреднённого сценария. Поэтому, испытав и сравнив их на реальном проекте, вы можете увидеть другие результаты.
Надо отметить, что сравнение веб-фреймворков может напоминать нечто вроде сравнения яблок с апельсинами. Вот ещё некоторые соображения, касающиеся React, Vue и Hyperapp, которые могут оказаться полезными при выборе конкретного фреймворка для некоего проекта:
Автор этого материала полагает, что, если вы дочитали до этого места, то у вас уже сформировалось понимание того, какой из исследованных здесь фреймворков лучше всего соответствует вашим нуждам. В конце концов, мы говорили не о том, какой из фреймворков является самым лучшим, а скорее о том, какой из них способен лучше всего показать себя в различных ситуациях. В итоге же можно сделать следующие общие выводы:
Уважаемые читатели! Как вы подбираете фреймворки для веб-проектов?
Автор: ru_vds
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/286637
Ссылки в тексте:
[1] полагает: https://hackernoon.com/i-abandonded-react-in-favor-of-hyperapp-heres-why-df65638f8a79
[2] Hyperapp: https://hyperapp.js.org/
[3] React: https://reactjs.org/
[4] Vue: https://vuejs.org/
[5] Image: https://habr.com/company/ruvds/blog/417483/
[6] API JSONPlaceholder: https://jsonplaceholder.typicode.com/
[7] js-framework-benchmark: https://github.com/krausest/js-framework-benchmark
[8] фрагментов: https://reactjs.org/docs/fragments.html
[9] компоненты высшего порядка: https://reactjs.org/docs/higher-order-components.html
[10] миксины: https://vuejs.org/v2/guide/mixins.html
[11] шаблонов: https://vuejs.org/v2/guide/syntax.html
[12] Источник: https://habr.com/post/417483/?utm_campaign=417483
Нажмите здесь для печати.