Re: «Сравнение JS-фреймворков: React, Vue и Hyperapp»

Это небольшая ответная статья на публикацию «Сравнение JS-фреймворков: React, Vue и Hyperapp». Вообще я не большой фанат подобных сравнений. Однако раз уж речь зашла о таком маргинальном фреймворке, как Hyperapp, в сравнении с мастодонтами, типа React и Vue, я подумал, почему бы не рассмотреть все те же примеры на Svelte. Так сказать, для полноты картины. Тем более, это займет буквально 5 минут. Поехали!

image

Если вдруг вы не знакомы со Svelte и концепцией исчезающих фреймворков, можете ознакомиться со статьями «Магически исчезающий JS фреймворк» и «Исчезающие фреймворки».

Для удобства читателей, я скопировал примеры из оригинальной статьи под спойлеры, чтобы было удобнее сравнивать. Что ж приступим.

Пример №1: приложение-счётчик


React
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"));


Vue
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" });


Hyperapp
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: работа с асинхронным кодом


React
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"));


Vue
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" });


Hyperapp
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 с помощью тегов