Re: «Сравнение JS-фреймворков: React, Vue и Hyperapp»
Это небольшая ответная статья на публикацию «Сравнение JS-фреймворков: React, Vue и Hyperapp». Вообще я не большой фанат подобных сравнений. Однако раз уж речь зашла о таком маргинальном фреймворке, как Hyperapp, в сравнении с мастодонтами, типа React и Vue, я подумал, почему бы не рассмотреть все те же примеры на Svelte. Так сказать, для полноты картины. Тем более, это займет буквально 5 минут. Поехали!
Если вдруг вы не знакомы со Svelte и концепцией исчезающих фреймворков, можете ознакомиться со статьями «Магически исчезающий JS фреймворк» и «Исчезающие фреймворки».
Для удобства читателей, я скопировал примеры из оригинальной статьи под спойлеры, чтобы было удобнее сравнивать. Что ж приступим.
Пример №1: приложение-счётчик
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 (
{this.state.count}
);
}
}
ReactDOM.render( , 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(
{this.count}
);
},
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) => (
{state.count}
);
app(state, actions, view, document.querySelector("#app"));
▍Svelte
{count}
→ Рабочий пример.
▍Анализ
Компонент Svelte — это html-файл, который имеет небезызвестный формат Single File Component (SFC), в том или ином виде, уже применяющийся в Vue, Ractive, Riot и некоторых других фреймворках. Кроме самого html-шаблона, компонент может иметь поведение и логику, описанную на javascript, а также scoped-стили компонента.
Ни одна часть компонента не является обязательной, поэтому компонент счетчика может состоять лишь из html-шаблона самого счетчика. Для изменения значения переменной count, обработчик кликов использует встроенный метод компонента set (), описанный в документации.
Пример №2: работа с асинхронным кодом
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 (
{this.state.posts.map(post => (
{post.title}
{post.body}
))}
);
}
}
ReactDOM.render( , 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 (
{this.posts.map(post => (
{post.title}
{post.body}
))}
);
},
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) => (
{state.posts.map(post => (
{post.title}
{post.body}
))}
);
app(state, actions, view, document.querySelector("#app"));
▍Svelte
{#each posts as {title, body}}
{title}
{body}
{/each}
→ Рабочий пример.
▍Анализ
В отличие от JSX, который, как под копирку, применяется во всех 3-х фреймворках из оригинального сравнения, и по сути расширяет javascript код html-подобным синтаксисом, Svelte использует более привычные возможности — внедрение js и css кода в html с помощью тегов и .
Скрипт компонента экспортирует простой JS объект, разделенный на секции. В секции methods описываются методы компонента, которые могут быть использованы через инстанс компонента и в обработчиках событий. Так, при нажатии на кнопку, вызывается метод getData (), внутри которого запрашиваются данные и по завершению операции, данные просто устанавливаются в стейт компонента и сразу же отрисовываются в шаблоне.
Обратите внимание, на использование деструктуризации объекта публикации (post) на каждом шаге итерации списка публикаций:
{#each posts as {title, body}}
Этот трюк позволяет избежать избыточности в шаблонах типа {post.title} и визуально упростить шаблоны, используя более короткую запись {title}.
Пример №3: компонент элемента списка для To-Do-приложения
function TodoItem(props) {
return (
props.toggle(props.id)}>
{props.value}
);
}
class TodoItem extends React.Component {
render () {
return (
this.props.toggle(this.props.id)}>
{this.props.value}
);
}
}
var TodoItem = Vue.component("todoitem", {
props: ["id", "value", "done", "toggle"],
template:
'{{value}} '
});
const TodoItem = ({ id, value, done, toggle }) = (
toggle(id)}>
{value}
);
▍Svelte
{value}
→ Рабочий пример.
▍Анализ
Тут все довольно банально. Выставляем css-класс в зависимости от значения done и меняем это значение на противоположное при клике на элемент списка.
Сравнение методов жизненного цикла компонентов
Disclaimer: С этого момента я решил опустить сравнение с Hyperapp, потому что иначе таблицы будут просто не читаемые.
Событие |
React |
Vue |
Svelte |
Инициализация |
constructor |
beforeCreate, created |
onstate |
Монтирование |
componentDidMount |
beforeMount, mounted |
oncreate, onupdate |
Обновление |
componentDidUpdate |
beforeUpdate, updated |
onstate, onupdate |
Размонтирование |
componentWillUnmount |
— | ondestroy |
Уничтожение |
— | beforeDestroy, destroyed |
— |
▍Анализ
Svelte крайне минималистичен, в том числе в плане хуков жизненного цикла. Существует всего 4 хука:
- onstate — вызывается до создания компонента и каждое изменение стейта до обновления DOM.
- oncreate — вызывается момент создания компонента.
- onupdate — вызывается сразу после монтирования в DOM и каждое изменение стейта после обновления DOM.
- ondestroy — вызывается при уничтожении компонента и удаления из DOM.
Сравнение производительности фреймворков
Честно говоря, не знаю что тут комментировать. Сами по себе бенчмарки и способ их исполнения всегда вызывает много споров, поэтому не думаю что имеет смысл излишне заострять внимание.
Однако других данных у нас все равно нет.
▍Работа с таблицами
▍Загрузка, запуск, размеры кода
▍Работа с памятью
▍Анализ
Cудя по цифрам, Svelte довольно быстрый, «жрет» мало памяти (в том числе потому что не использует VirtualDOM), быстро запускается и имеет небольшие размеры.
Вообще, сказать что результаты бенчмарков этих 3-х фреймворков отличались координально, я не могу. Однако, только Svelte имеет исключительно «зеленый» столбик, т.е. он достаточно хорош сразу по всем показателям, а значит отлично сбалансирован и не имеет явных перекосов ни в скорости, ни в потреблении памяти и иных метриках. В общем, с ним смело можно начинать любой проект, от обычного веба, до мобильных устройств, Смарт ТВ и более экзотичных систем.
Итоги
Svelte — отличный инструмент для создания компонентов практически любых веб-приложений. Он такой же мощный как React и хотя пока имеет значительно меньшее сообщество, при этом требует меньше усилий для освоения. Он гибок, как Vue, при этом значительно более минималистичен и выверен, так как не пытается ухватиться сразу за все идеи современной веб-разработки.
Кроме того, он единственный использует концепцию исчезающих фреймворков. Иными словами он не имеет никакого специфического рантайма, кроме самого браузера.
Приложения на нем получаются быстрыми, не требовательными к ресурсам и маленькими в размере. Кроме того, Svelte может «бесшовно» использоваться в приложениях, написанных на других фреймворках, о чем я планирую написать в следующий раз.
Если вы впервые познакомились с этим замечательным фреймворком, возможно вам будет интересно прочитать и другие статьи о нем:
→ Магически исчезающий JS фреймворк
→ 1Kb autocomplete
→ SvelteJS: Релиз второй версии
Если у вас остались вопросы по Svelte, вступайте в русскоязычный телеграм канал SvelteJS. Там вы всегда сможете задать вопрос и получить совет, узнать последние новости, а также просто приятно пообщаться. Будем вам рады!