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

https://hackernoon.com/javascript-framework-comparison-with-examples-react-vue-hyperapp-97f064fb468d
  • Перевод
Автор материала, перевод которого мы сегодня публикуем, полагает, что Hyperapp — это заслуживающая внимания альтернатива таким веб-фреймворкам, как React или Vue. Он говорит, что причиной такого утверждения стало то, что он выяснил, что Hyperapp легче в освоении, чем эти два фреймворка. Его идея подверглась критике, так как кажется, что основана она исключительно на его мнении, а такой подход попросту не даёт другим фреймворкам возможности показать их сильные стороны. Эта статья направлена на объективный анализ Hyperapp, React и Vue, проведённый на основе простых примеров, демонстрирующих их возможности, и на основе результатов их испытаний.



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


Реализация приложения-счётчика, вероятно, является одним из самых часто используемых примеров в реактивном программировании. Он предельно прост и понятен:

  • Нам понадобится переменная count, которая будет хранить значение счётчика.
  • Нужны будут два метода, позволяющих инкрементировать и декрементировать переменную count.
  • Требуется механизм для вывода значения, хранящегося в count и представления его пользователю.
  • Нужны две кнопки, привязанные к соответствующим методам, позволяющие пользователю воздействовать на переменную count.

Вот реализация этого примера с использованием рассматриваемых фреймворков.

▍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 (
            <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"));

▍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(
            <div>
                <h1>{this.count}</h1>
                <button onClick={() => this.down(1)}>-</button>
                <button onClick={() => this.up(1)}>+</button>
            </div>
        );
    },

    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) => (
    <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.
  • В React используется объектно-ориентированная парадигма. Тут создаётся класс для компонента Counter. Vue идёт похожим путём. Тут создаётся новый экземпляр класса Vue, ему передаётся информация. И, наконец, в Hyperapp применяется функциональная парадигма, здесь используются самостоятельные сущности view, state и actions.
  • Если говорить о переменной count, то в React она инициализируется в конструкторе компонента, а в Vue и Hyperapp она представляет собой свойство, соответственно, объектов data и state.
  • Если продвинуться в исследовании этих приложений дальше, то можно заметить, что в React и Vue используются очень похожие методы для взаимодействия с переменной count. В React, для изменения состояния приложения, применяется метод setState, унаследованный от React.Component. В Vue значение this.count изменяется напрямую. Методы в Hyperapp написаны с использованием синтаксиса стрелочных функций ES6. Среди рассматриваемых фреймворков он единственный использует подобное, так как React и Vue вынуждены использовать внутри своих методов ключевое слово this. Методы Hyperapp, с другой стороны, требуют, чтобы им, в качестве аргумента, передавали бы объект с состоянием приложения. Это означает, что их, вероятнее всего, можно будет повторно использовать в различных контекстах.
  • Та часть приложения, которая отвечает за вывод данных на страницу, во всех трёх примерах выглядит практически одинаково. Особенность Vue заключается в том, что при использовании этого фреймворка в подсистему рендеринга надо передать функцию h. В Hyperapp, вместо onClick, используется onclick, а так же здесь обращение к переменной count осуществляется не так как в React и Vue, что обусловлено особенностями того, как в каждом из фреймворков реализовано хранение состояния приложения.
  • И, наконец, все три фреймворка используют привязку к элементу #app. В каждом из них эта операция выполняется по-разному. Надо отметить, что в Vue эта операция выглядит самой простой и понятной и даёт разработчику более гибкую конструкцию, работая с селектором элемента, а не с самим элементом.

▍Выводы


Если напрямую сравнить код, решающий одну и ту же задачу, написанный с использованием всех трёх фреймворков, то окажется, что Hyperapp, для реализации приложения-счётчика, требуется меньше всего строк кода, и это — единственный фреймворк, использующий функциональный подход. Однако, объём кода, написанный c использованием Vue, если считать количество символов, получается немного меньше, да и использование в нём селектора элемента выглядит очень хорошо. Код React-приложения кажется самым длинным, но это не означает, что его понять намного сложнее, чем код, написанный для работы с другими анализируемыми фреймворками.

Пример№2: работа с асинхронным кодом


Вполне возможно, что вам, на практике, придётся иметь дело с асинхронным кодом. Одна из самых распространённых асинхронных операций представляет собой отправку запроса некоему API. Для целей этого примера применяется API JSONPlaceholder, содержащее условные данные и выдающее список публикаций. Вот что мы собираемся здесь сделать:

  • Сохраним массив для размещения в нём публикаций (posts) в состоянии приложения.
  • Вызовем, воспользовавшись подходящим методом, fetch(), указав нужный нам URL, подождём поступления данных, распарсим полученный JSON-код, представляющий собой массив объектов, и, наконец, обновим переменную posts, записав в неё полученные данные.
  • Выведем на страницу кнопку, которая вызывает метод, загружающий список публикаций.
  • Выведем список публикаций из posts с использованием ключей.

Рассмотрим код, реализующий вышеописанную схему действий.

▍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 (
            <div>
                <button onClick={() => this.getData()}>Get posts</button>
                {this.state.posts.map(post => (
                    <div key={post.id}>
                        <h2><font color="#3AC1EF">{post.title}</font></h2>
                        <p>{post.body}</p>
                    </div>
                ))}
            </div>
        );
    }
}

ReactDOM.render(<PostViewer />, 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 (
            <div>
                <button onClick={() => this.getData()}>Get posts</button>
                {this.posts.map(post => (
                    <div key={post.id}>
                        <h2><font color="#3AC1EF">{post.title}</font></h2>
                        <p>{post.body}</p>
                    </div>
                ))}
            </div>
        );
    },

    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) => (
    <div>
        <button onclick={() => actions.getData()}>Get posts</button>
        {state.posts.map(post => (
            <div key={post.id}>
                <h2><font color="#3AC1EF">{post.title}</font></h2>
                <p>{post.body}</p>
            </div>
        ))}
    </div>
);

app(state, actions, view, document.querySelector("#app"));

▍Анализ


Разберём этот код и сравним три исследуемые фреймворка.

  • Так же, как и в предыдущем примере, хранение состояния приложения, вывод данных и подключение к элементу страницы, во всех трёх фреймворках весьма схожи. Тут наблюдаются те же различия, о которых мы уже говорили выше.
  • Загрузка данных с помощью функции fetch() — операция довольно простая, она работает так, как ожидается, во всех фреймворках. Основное различие здесь, однако, заключается в том, что Hyperapp поддерживает выполнение асинхронных операций немного не так, как другие фреймворки. Вместо того чтобы модифицировать состояние напрямую, внутри асинхронного действия, это действие вызывает другое, синхронное действие, которое получает данные и преобразует их в подходящий формат. Это делает ядро приложения более функциональным и лучше подходящим для разбиения на небольшие части, которые, потенциально, подходят для повторного использования. Такой подход, кроме того, помогает избегать некоторых проблем, свойственных вложенным коллбэкам, которые могут возникнуть в ситуациях, подобных рассматриваемым.
  • Если говорить о размерах кода, то Hyperapp-приложению снова нужно меньше строк кода для достижения той же цели, но код на Vue выглядит более кратким, и, если посчитать количество символов кода, он короче других вариантов.

▍Выводы


Выполнение асинхронных операций оказалось одинаково простым во всех фреймворках. Hyperapp может склонить разработчика к написанию более функционального и модульного кода при работе с асинхронными действиями, но два других фреймворка тоже отлично справляются с поставленной перед ними задачей, и, в этом плане, дают разработчику возможность выбора.

Пример №3: компонент элемента списка для To-Do-приложения


Вероятно, To-Do-приложения — это самый знаменитый пример в области реактивного программирования. По всей видимости, нечто подобное реализовано с использованием почти каждого из существующих фреймворков. Тут мы не будем реализовывать всё приложение. Вместо этого остановимся на простом компоненте без состояния для того, чтобы изучить возможности исследуемых фреймворков по созданию небольших строительных блоков веб-приложений, подходящих для повторного использования.

Рассмотрим реализацию компонента с применением исследуемых фреймворков. В этом примере мы, однако, расширим рассматриваемые варианты кода за счёт рассмотрения React-компонента, написанного в функциональном стиле.

▍React (функциональный стиль)


function TodoItem(props) {
    return (
        <li class={props.done ? "done" : ""} onclick={() => props.toggle(props.id)}>
            {props.value}
        </li>
    );
}

▍React


class TodoItem extends React.Component {
    render () {
        return (
            <li class={this.props.done ? "done" : ""} onclick={() => this.props.toggle(this.props.id)}>
                {this.props.value}
            </li>
        );
    }
}

▍Vue


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


Обратите внимание на то, что Hyperapp так же использует функциональный стиль.

const TodoItem = ({ id, value, done, toggle }) = (
    <li class={done ? "done" : ""} onclick={() => toggle(id)}>
        {value}
    </li>
);

▍Анализ


  • React, в том, что касается использования паттернов кодирования, является самым гибким фреймворком. Он поддерживает функциональные компоненты, а так же компоненты, оформленные в виде классов. Кроме того, React, в его стандартном виде, поддерживает и компоненты Hyperapp.
  • Hyperapp тоже поддерживает функциональные React-компоненты. Это означает, что при работе с Hyperapp и React имеется большое пространство для экспериментов.
  • Vue в этом испытании занимает последнее место. У него довольно странный синтаксис, который непросто сразу понять даже тем, кто знаком с React или Hyperapp.
  • Если говорить о длине кода, то все примеры имеют очень похожие размеры. Единственное, что тут можно отметить — это то, что код на React, в одном из вариантов, получился немного объёмнее, чем в другом.

▍Выводы


Для того чтобы привыкнуть к 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. С их помощью у программиста есть возможность обработать всё, что происходит с компонентом, либо до вызова соответствующего события, либо после. Это может оказаться кстати для управления сложными компонентами.
  • Хуки жизненного цикла React и Hyperapp очень похожи, хотя в React объединяет обработку событий, возникающих при размонтировании и уничтожении компонента, а Hyperapp так же поступает с событиями создания и монтирования компонента. И тот и другой дают разработчику достаточное количество возможностей по обработке событий жизненного цикла.
  • Vue не обрабатывает событие размонтирования (насколько это можно понять, проанализировав API фреймворка), вместо этого полагаясь на хуки, связанные с уничтожением компонента. React не обрабатывает событие уничтожения компонента, позволяя обрабатывать лишь событие размонтирования компонента. Hyperapp не предлагает хуков для обработки события создания компонента, вместо этого полностью полагаясь на событие монтирования. В зависимости от ваших нужд и опыта эти различия стоит учитывать при проектировании приложения с учётом возможности обработки событий жизненного цикла компонентов.

▍Выводы


В целом можно отметить, что способы обработки событий, возникающих в ходе жизненного цикла компонентов, поддерживают все фреймворки. Эти способы позволят решать множество задач. Все три фреймворка предлагают хуки для всех видов событий, но между ними есть незначительные отличия, источником которых могут служить внутренние особенности фреймворков и различия в их реализации. Пожалуй, Vue в этой области находится на шаг впереди других систем, предлагая более детализированную систему обработки событий, позволяя обрабатывать события жизненного цикла либо до их возникновения, либо после.

Сравнение производительности фреймворков


Помимо удобства использования фреймворка и применяемых при работе с ним приёмов программирования, многих разработчиков серьёзно заботит и производительность фреймворков, особенно — для достаточно сложных приложений. Ценным источником сведений о производительности различных фреймворков служит проект js-framework-benchmark.

Поэтому взглянем на результаты тестирования React, Vue и Hyperapp.

▍Работа с таблицами


Вот результаты испытаний фреймворков на предмет работы с таблицами. Показатель в ячейках таблицы соответствует длительности выполнения операции ± среднеквадратическое отклонение. В скобках приведён результат деления полученного показателя на самый лучший показатель.


Анализ


  • Операции, в которых для вывода данных не используются ключи (non-keyed) оказываю гораздо быстрее операций, в которых ключи применяются (keyed).
  • Самым быстрым из всех шести рассмотренных вариантов оказался вариант, в котором используется React без применения ключей, показывающий впечатляющую производительность во всех тестах.
  • Если сравнить Vue и React при работе с использованием ключей, то у Vue тут имеется небольшое преимущество. В то же время, если сопоставить React и Vue в вариантах, где ключи не используются, Vue показывает значительно меньшую производительность, чем React.
  • У Vue и Hyperapp, как можно видеть из результатов, имеются какие-то сложности с тестом, в котором производится частичное обновление таблицы (partial update), а React показывает себя на нём хорошо, вероятно, из-за некоей оптимизации, направленной на ускорение подобных операций.

▍Загрузка, запуск, размеры кода


Вот таблица с результатами исследования показателей, имеющих отношение к скорости запуска фреймворка, к его размерам, к использованию им главного потока.


Анализ


  • Код Hyperapp оказался самым маленьким среди исследуемых фреймворков. Код React и Vue имеет примерно одинаковые размеры.
  • Hyperapp нужно меньше всего времени на запуск. Причиной этого, определённо, является маленький размер кода фреймворка и минималистичный подход в проектировании его API.
  • Vue, если говорить о времени, необходимом на запуск фреймворка, оказывается немного быстрее, чем React.

▍Работа с памятью


Теперь рассмотрим результаты тестирования выделения памяти.


Анализ


  • Hyperapp оказывается самым нетребовательным фреймворком в плане потребления памяти.
  • В целом можно отметить, что все фреймворки потребляют не особенно много памяти. Это говорит о том, что они, на современных компьютерах, будут работать примерно одинаково.

▍Выводы


Если, при разработке некоего проекта, нужно добиться максимальной производительности, вам стоит разобраться, во-первых, с тем, что за приложение вы разрабатываете, а во-вторых — чётко выяснить нужды этого приложения. Если свести воедино анализ производительности всех трёх фреймворков, то возникает такое ощущение, что Vue и React лучше подходят для более сложных приложений, а Hyperapp лучше показывает себя на приложениях меньшего масштаба, в которых нужно обрабатывать меньше данных, которым требуется как можно более быстрый запуск, и которым может понадобиться работать на не самых быстрых компьютерах.

Однако тут стоит помнить, что использованные здесь тесты производительности далеки от реальной жизни, от некоего усреднённого сценария. Поэтому, испытав и сравнив их на реальном проекте, вы можете увидеть другие результаты.

Дополнительные замечания


Надо отметить, что сравнение веб-фреймворков может напоминать нечто вроде сравнения яблок с апельсинами. Вот ещё некоторые соображения, касающиеся React, Vue и Hyperapp, которые могут оказаться полезными при выборе конкретного фреймворка для некоего проекта:

  • React обходит проблему, связанную с тем, что соседние JSX-элементы должны быть обёрнуты в родительский элемент, вводя концепцию фрагментов — элементов, которые позволяют группировать набор элементов-потомков без добавления в DOM дополнительных узлов.
  • React даёт в распоряжение разработчика компоненты высшего порядка, в то время как в Vue, для целей повторного использования функционала компонентов, применяются миксины.
  • Vue более полно задействует концепцию разделения ответственности, разделяя структуру и функционал приложения с использованием шаблонов.
  • Hyperapp, если сравнивать его с React и Vue, выглядит как система, предоставляющая API более низкого уровня. Код Hyperapp-приложений оказывается короче, он даёт большую гибкость, что может быть полезно в тех случаях, когда разработчику может захотеться заняться его тонкой настройкой и исследованием его внутренних механизмов.

Итоги


Автор этого материала полагает, что, если вы дочитали до этого места, то у вас уже сформировалось понимание того, какой из исследованных здесь фреймворков лучше всего соответствует вашим нуждам. В конце концов, мы говорили не о том, какой из фреймворков является самым лучшим, а скорее о том, какой из них способен лучше всего показать себя в различных ситуациях. В итоге же можно сделать следующие общие выводы:

  • React — это очень мощный инструмент, вокруг него сложилось большое сообщество разработчиков, которое, если у вас возникнут какие-то проблемы, вполне может помочь вам в их решении. Этот фреймворк не так уж и сложно освоить, но, для того, чтобы овладеть им в совершенстве, понадобится немало времени. Однако, React — замечательный фреймворк и он стоит усилий, потраченных на его изучение.
  • Vue может выглядеть немного странным, если раньше вы использовали ещё какой-нибудь JavaScript-фреймворк, но это — весьма интересный инструмент. Он является достойной альтернативой React, и его, вполне возможно, стоит изучить, если React вам по каким-то причинам не вполне подходит. Vue имеет некоторые очень хорошие встроенные возможности, а его сообщество растёт, вероятно, даже быстрее, чем сообщество React.
  • И, наконец, Hyperapp — это маленький приятный фреймворк, подходящий для небольших проектов, а так же — для начинающих разработчиков. Набор предоставляемых им инструментов меньше, чем у React или Vue, но его возможностей достаточно для быстрой разработки приложений или их прототипов. В ходе работы с ним можно разобраться с основами, на которых базируются другие фреймворки. Немалые объёмы кода, написанного для Hyperapp, совместимы с другими двумя фреймворками, причём, они могут использоваться там либо в том же виде, в котором присутствуют в Hyperapp-приложениях, либо с незначительными изменениями. В результате, если, начав проект на Hyperapp, вы поймёте, что вам нужно нечто большее, то, параллельно осваивая React или Vue, вы сможете без особых проблем перейти на другой фреймворк.

Уважаемые читатели! Как вы подбираете фреймворки для веб-проектов?


Всё верно, это промо-код для скидки в 10% на наши виртуальные сервера :)

RUVDS.com

912,00

RUVDS – хостинг VDS/VPS серверов

Поделиться публикацией

Похожие публикации

Комментарии 65
    +8
    Откровенно говоря все эти «сравнения фреймворков» уже в печенки сидят.
      +2
      Ну надо же как то поддерживать хайп по JS'у.
      0
      Я так понимаю у Hyperapp никакого Virtual DOM нет, но при этом он сравним по производительности с React и Vue, судя по цифрам в вышеприведенных табличках. Тогда где же все это хваленное преимущество наличия Virtual DOM?
        +1
        Окей, вроде есть VDOM, не внимателен.
        0
        Подскажите пожалуйста, чем отличается keyed от non-keyed?
          +1
          Видимо имеется ввиду атрибут key: <Component key='unique-key'>. React использует его для того, чтобы рендерить соседние компоненты в правильном порядке
            0

            Вот здесь хорошо описано различие: https://www.stefankrause.net/wp/?p=342

            0
                    .then(json => {
                        actions.getDataComplete(json);
                    });
                },
                getDataComplete: data => state => ({ posts: data })

            может actions.getDataComplete(json)(state) или return actions.getDataComplete(json);? А то в текущем виде промис ничего не возвращает, только создает замыкание state => ({ posts: data }), которое потом выбрасывается.

            > Он говорит, что причиной такого утверждения стало то, что он выяснил, что Hyperapp легче в освоении, чем эти два фреймворка.

            А уже освоенный vue/react еще легче в освоении.
              0

              Промису и не надо ничего возвращать, он вызывает действие. Точно так же как это делает обработчик клика по кнопке.

                0
                может actions.getDataComplete(json)(state)

                Хех. Я на Hyperapp не пишу, так что тоже глаз цепился. Думал — ошибка в примере.


                Но таки нет. Обратите внимание, что в здесь actions — это не глобальная переменная, а то, что в передалось в обработчик вторым параметром.
                Это обертка над оригинальным объектом actions, где обработчики автоматически привязаны к state (см. исходный код)

                +4
                И, наконец, все три фреймворка используют привязку к элементу #app. В каждом из них эта операция выполняется по-разному. Надо отметить, что в Vue эта операция выглядит самой простой и понятной и даёт разработчику более гибкую конструкцию, работая с селектором элемента, а не с самим элементом.


                Т.е. передать селектор это удобнее чем самому подобрать элемент и отдать фреймворку? Точно?
                В случае если я отдаю элемент я сам решаю как его выбирать, возможно я даже его буду сам динамически создавать, подцеплять в DOM и потом уже инитить фреймворк. В случае селектора — извините, запихайте пожалуйста вначале элемент в разметку, а потом отдайте селектор фреймворку чтобы он опять пошел щупать DOM на предмет элемента, на котороый у меня вроде как уже ссылка есть.
                  0
                  В случае если я отдаю элемент я сам решаю как его выбирать, возможно я даже его буду сам динамически создавать

                  Создаешь в нужном месте div с нужным id, потом инициализируешь vue.
                    +1
                    Создаешь в нужном месте div с нужным id, потом инициализируешь vue.


                    именно так, а Vue лезет в DOM и ищет этот div, мне кажется тут присутствует некоторая избыточность — если бы я мог просто передать ссылку на элемент то и искать его в DOM было бы незачем

                    похоже что разработчики Vue смотрят на ситуацию под схожим углом — если посмотреть в доки Vue, то окажется что в параметр el можно передавать не только селектор, но HTMLElement (https://vuejs.org/v2/api/#el)
                    +2
                    Параметр «el» в vue может принимать как строку-селектор, так и объект типа HTMLElement. Так что всё нормально здесь, автор статьи был либо невнимателен, либо предвзят =)
                    +8
                    Прямо по примеру №1. Очень смелое утверждение:
                    то окажется, что Hyperapp, для реализации приложения-счётчика, требуется меньше всего строк кода
                    при том, что для Hyperapp использованы стрелочные функции, в отличие от reart/vue:

                    down: value => state => ({ count: state.count - value}),

                    down: function(value) {
                        this.count -= value;
                    },
                      +7
                      Пример странный. У меня получилось так:
                      import Vue from "vue/dist/vue";
                      
                      new Vue({
                          data: { count: 0 },
                      
                          template: `
                              <div>
                                  <h1>{{count}}</h1>
                                  <button @click="count -= 1">-</button>
                                  <button @click="count += 1">+</button>
                              </div>
                          `,
                      
                          el: "#app"
                      });
                      
                        +2
                        @click="count -= 1"

                        Там внутри что-то типа with(this){ return eval(attr); } или new Function("with(this) ...")?

                          0
                          Весь шаблон обернут в with от контекста. Т.к. шаблон во втором вью сначала компилируется в JS, а затем используется. Причем компилятор — опциональный в сборке (если использовать single file component, то шаблоны уже будут в виде JS в бандле, ровно как в реакте). Фактически шаблоны во втором vue.js — сахар над рендер-функциями.
                            +1
                            Весь шаблон обернут в with от контекста.

                            То есть, стоит опечататься в названии переменной и мы пишем уже не в контекст компонента, а в случайный контекст выше по иерархии вплоть до window?
                              +1

                              Кстати говоря, with запрещён к использованию в strict-mode и вообще не рекомендован к пользованию в статье про него на MDN.


                              Поэтому, даже несмотря на то, что он используется не напрямую, а скрыт внутри фреймворка, это не очень хорошо.

                                +1

                                Апдейт: написал небольшой бенчмарк, проверил, насколько использование with просаживает производительность: https://esbench.com/bench/5b54ae36f2949800a0f61eb9



                                Проверил в Chrome и Firefox, разница на порядок, даже без использования "use strict". Если Vue действительно использует with в продакшен-сборке, то где-то здесь зарыт неплохой буст его производительности.

                        +3
                        А почему в vue используется jsx? более аутентичным было бы использование шаблонов.
                          –1
                          в этом сравнении как-то упустили размеры фреймворков, реакт весит 160кб, а hyperapp — 5кб. И для мобильников это существенная разница, бандл на мегабайт жаваскрипта инициализируется на телефоне секунд 5
                            +2
                            реакт весит 160кб

                            React, конечно не 5 KiB весит (тут вам Preact может помочь), но уж точно не 160, с чего вы взяли. Вы development флаг отключить забыли?

                              +3
                              https://unpkg.com/react@16.4.1/umd/react.production.min.js
                              7.0 KiB (plain), 2.9 KiB (gzipped)

                              https://unpkg.com/react-dom@16.4.1/umd/react-dom.production.min.js
                              94.2 KiB (plain), 29.6 KiB (gzipped)

                              = 101.2 KiB (plain), 32.5 KiB (gzipped).


                              Собственно вот. Не знаю что именно вы имели ввиду под 160 KiB — с gzip или без, но всё не так плохо. А для компактных небольших приложений вполне сгодится не только hyperapp, но и preact. Там вроде как размеры вполне сопоставимые.


                              Для сравнения, 3.3.1 версия jQuery весит 256 KiB -> 75.2 KiB. Благо её использование обычно это legacy проекты и реальной нужды использовать jQuery в новых продуктах уже давно нет.

                                0
                                точно, завтыкал, предыдущая версия реакта была 160кб
                                  +1

                                  Я думаю тогда уж лучше сразу Svelte брать.

                                  0

                                  А Svelte вообще ничего не весит… :)

                                    +2
                                    насколько я понял, чем больше компонентов в приложении, тем больше он весит, и размер растет довольно быстро
                                      0
                                      1. Самого Svelte нету, вес есть только у бандла т.е. по факту вашего кода.
                                      2. Больше компонентов, а значит больше вес это и для любых других фреймворков актуально. Тот код который генерирует компилятор Svelte очень хорошо сжимается и минифицируется в итоге разница не очень большая но самое главное то что в 50-70кб кода можно уложить ВСЁ ваше приложение (с 40-50 жирными компонентами) тогда как у многих рассматриваемых тут фреймворков вес без пользовательского кода уже куда больше.
                                      3. А если вам надо реально делать огромное приложение то в любом случае его надо разбивать на разные части (микрофеймворки и это всё).

                                      В результате получается гораздо лучше. Единсвенный минус это не самое обширное коммьюнити, а значит и набор готовых решений не очень обширный, но это дело наживное, а главное каждый может ещё успеть засветится на новом поприще. :)

                                        +3
                                        ок, я полез тестировать, взял вот этот проект github.com/charpeni/svelte-example, собрал, минифицировал… получилось 6.2кб
                                        исходного кода там на 2кб без минификации, оно получается уже наровне с hyperapp по размеру бандла. Пара десятков компонентов, и бандл Svelte будет в несколько раз больше чем то же приложение с hyperapp
                                          0
                                          1. Это достаточно старый пример для первого Svelte. (в новом много чего добавили и улучшили)
                                          2. Попробуйте всё же добавить пару компонентов. Как я писал выше код там крайне примитивный (и эффективный) генерируется и он очень хорошо сжимается. Просто все эти createElement и appendNode ужмуться во что то типо a(), b() .
                                          3. А я детальнее глянул на hyperapp честно говоря это больше походит на каркас для VanilaJS и мягко говоря с примитивным View. Ну т.е. тут просто не сопостовимые проекты по фичам и я не спорю что можно и hyperapp заюзать и получить результат но вопросс в удобстве. Сейчас Svelte близок к Reactive и Vue по фичам (а где то и больше).
                                          4. Опять же по оперативке Svelte сильно выигрывает т.к. нету VDOM и не надо дважды хранить информацию.

                                          т.е. выходит что Svelte позволяет писать с фичами макрофреймворков но с размерами микрофреймворков.

                                            0
                                            1. Можно ссылку на более актуальный пример?
                                            2. Приложение которое я тестировал собирается в 12кб, затем минифицируется до 6кб. С обычными библиотеками всегда понятен оверхед, здесь же размер растет нелинейно, непонятно до какого момента выгоднее использовать Svelte, а не реакт
                                            3. Статья показывает, что hyperapp справляется со стандартными задачами не хуже взрослых фреймворков, что есть такое в Svelte, чего нет в hyperapp?
                                              0
                                                –1

                                                И на какой пункт отвечает данная статья? Я в ней не увидел ни ссылки на проект, который можно легко сбилдить, ни какого-либо анализа размера получающегося приложения, ни преимуществ над реактом или hyperapp

                                                  0
                                                  Так то я надеялся, что статья вас заинтересует как продолжение этой статьи. Ну и думал, может вы посмотрите ссылки на другие статьи по Svelte. Там это обсуждалось и не раз.

                                                  Хотя вполне могу помочь вам сократить изыскания, например, вот:
                                                  A Real-World Comparison of Front-End Frameworks with Benchmarks.

                                                  Там есть и про размеры в том числе.
                                              +1
                                              т.е. выходит что Svelte позволяет писать с фичами макрофреймворков но с размерами микрофреймворков.

                                              Очевидно, что если в микрофреймворк добавить фичи макрофреймворка, то получится макрофреймворк. С размером макрофреймворка.

                                                +1

                                                да, но т.к. Svelte это компилятор то все эти фичи не влияют на рантайм.

                                                  0
                                                  то все эти фичи не влияют на рантайм.

                                                  Но ведь влияют. Чем больше у вас фич, тем больше будет вес итогового бандла.

                                                    0
                                                    Конечно влияют. Просто в случае с классическими фреймворками, вы тащите на клиент все что шипит фреймворк, даже если вам это не нужно. В случае с «исчезающими» таких проблем нет, а значит практически исчезает такая штука как «feature cost».

                                                    Поэтому в итоге все же нет. Если в микрофреймворк добавить фичи макрофреймворка, а потом все это дело статически проанализировать и потрясти хорошенько, то получится код вашего приложения и только он.
                                                      0
                                                      Конечно влияют. Просто в случае с классическими фреймворками, вы тащите на клиент все что шипит фреймворк, даже если вам это не нужно.

                                                      Это понятно. НО мы же в этой ветке говорим о: "Svelte позволяет писать с фичами макрофреймворков но с размерами микрофреймворков". Если вы используете все фичи — то и в бандле будут все фичи. Ничего "исчезнуть" не выйдет.


                                                      Если в микрофреймворк добавить фичи макрофреймворка, а потом все это дело статически проанализировать и потрясти хорошенько

                                                      То ничего не вытрясется, т.к. если вы используете фичу Х, то вытрясти ее нельзя.

                                                        0
                                                        Это понятно. НО мы же в этой ветке говорим о: «Svelte позволяет писать с фичами макрофреймворков но с размерами микрофреймворков». Если вы используете все фичи — то и в бандле будут все фичи. Ничего «исчезнуть» не выйдет.

                                                        Не знаю о чем говорите вы, но я так понял мысль stalkerg:

                                                        Фичи у Svelte как у макрофреймворка, только добавляются они к вам в бандл только по необходимости. Если вы берете классический макрофреймворк, то тащите сразу все фичи, даже если они вам никогда не понадобятся. У вас даже выбора нет особого.

                                                        В мире в котором нет Svelte, у разработчика есть лишь 2 выбора:

                                                        1) сразу брать макрофреймворк, чтобы уж точно все было, мало ли что понадобиться и сразу платить за это фича-косты.
                                                        2) надеется, что хватит фич микрофреймворка и не придется сбоку-припеку костылить что-то еще или переписывать код на макрофреймворк, и при этом иметь минимальные фича-косты

                                                        Хорошо что мы живем не в этом мире, так как со Svelte у нас есть 3-й вариант:

                                                        3) Берем сразу макрофреймворк (по фичам), но юзаем только то, что нужно и при этом не платим костов за ненужные фичи, также как в случае с микрофреймворком.

                                                          –1
                                                          > Не знаю о чем говорите вы, но я так понял мысль stalkerg:

                                                          То, о чем вы говорите, это: «Svelte позволяет писать с фичами _микро_фреймворков и размерами _микро_фреймворков».
                                                          Ангуляр, например, позволяет писать с фичами микрофреймворков, но не позволяет писать с размерами микрофреймворков (по крайней, мере пока Ivy не доделают). Svelte позволяет.
                                                          И ничего не позволит писать с фичами макрофреймворков и размерами микро, потому что если вы пишите с фичами макро — значит, они в коде. Вы их используете. Убрать их нельзя.
                                                            –1
                                                            Не стоит вводить людей в заблуждение, утверждая, что Svelte — единственный микромодульный фреймворк. Не единственный. Более того, Svelte — всего-лишь низкоуровневый фреймворк, то есть предоставляет лишь базовую инфраструктуру, без хорошо интегрированной библиотеки модулей и компонент. Вы не сможете на нём поднять полноценный проект за один день.
                                      0

                                      Было бы интересно про более сложные концепции, типа render props, HOC и т.п.

                                        0
                                        Про render props во вью — там есть концепт scoped slots; с HOC же всё настолько печально-неудобно, что ой (кратко — очень сложный createElement вызов, очень много всего прокидывать).
                                        0
                                        Вспоминаются выборы президента в Футураме:
                                        — Они похожи, как близнецы!
                                        — Да они и есть близнецы…
                                          –7

                                          Эх, опять не добавили к сравнению $mol. Исправляем ситуацию...


                                          Пример№1: приложение-счётчик
                                          $my_counter $mol_view
                                              sub /
                                                  <= Count $mol_view sub / <= count?val 0
                                                  -
                                                  <= Down $mol_button
                                                      title \-
                                                      click?event <=> down?event null
                                                  -
                                                  <= Up $mol_button
                                                      title \+
                                                      click?event <=> up?event null

                                          namespace $.$$ {
                                              export class $my_counter extends $.$my_counter {
                                          
                                                  down() {
                                                      this.count( this.count() - 1 )
                                                  }
                                          
                                                  up() {
                                                      this.count( this.count() + 1 )
                                                  }
                                          
                                              }
                                          }

                                          Как видим, кода меньше, чем у конкурентов и он содержит меньше визуального шума. Нет никаких импортов. Парадигма объектно-ориентированная. Декларативное и логическое описания разделены.


                                          Пример№2: работа с асинхронным кодом

                                          В предложенных в статье реализациях где-то потерялось отображение индикатора загрузки и обработка ошибок загрузки. Если их добавить, то кода станет куда больше. Мы же не будем халтурить и сделаем всё как следует:


                                          $my_posts $mol_list
                                              loads?val 0
                                              rows /
                                                  <= Load $mol_button
                                                      title \Get posts
                                                      click?event <=> load?event null
                                                  <= Posts /
                                              Post $mol_view sub /
                                                  <= Post_title $mol_view sub / <= post_title \
                                                  <= Post_body $mol_view sub / <= post_body \

                                          namespace $.$$ {
                                              export class $my_posts extends $.$my_posts {
                                          
                                                  @ $mol_mem
                                                  posts() {
                                                      if( !this.loads() ) return []
                                                      return this.$.$mol_http.resource( `https://jsonplaceholder.typicode.com/posts?${ this.loads() }` ).json()
                                                  }
                                          
                                                  load() {
                                                      this.loads( this.loads() + 1 )
                                                  }
                                          
                                                  Posts() {
                                                      return this.posts().map( ( post , index )=> this.Post( index ) )
                                                  }
                                          
                                                  post_title( index : number ) {
                                                      return this.posts()[ index ].title
                                                  }
                                          
                                                  post_body( index : number ) {
                                                      return this.posts()[ index ].body
                                                  }
                                          
                                              }
                                          }

                                          Как видно, для отображения индикатора загрузки и сообщения об ошибке нам ничего не пришлось дополнительно писать, так как фреймворк взял эту рутину на себя. Логика загрузки мы описали в реактивном стиле, что даёт надёжность. Например, если пользователь будет неистово кликать по кнопке, то ранее посланные запросы будут отменяться, никак не влияя на работу приложения. Чего не скажешь про решения из статьи, где в этом случае на странице начнётся дискотека. Объём кода у всех сопоставимый.


                                          Пример №3: компонент элемента списка для To-Do-приложения
                                          $my_todo_item $mol_view
                                              attr * my_todo_item_done <= done?val false
                                              event * click?event <=> toggle?event null
                                              sub / <= title \

                                          Мы получили самую комппактную реализацию, которая, в отличие от предложенных в статье решений, не создаёт новые обработчики событий при каждом рендеринге. Кроме того, архитектура $mol позволяет не выносить реализацию toggle во вне компонента, так что её можно описать тут же:


                                          namespace $.$$ {
                                              export class $my_todo_item extends $.$my_todo_item {
                                          
                                                  toggle() {
                                                      this.done( !this.done() )
                                                  }
                                          
                                              }
                                          }

                                          При этом владелец может переопределить как реализацию done, так и toggle.


                                          Сравнение методов жизненного цикла компонентов

                                          Жизненный цикл совершенно иной. Любые объекты создаются (constructor), когда кому-то нужны и разрушаются (destructor), когда никому не нужны. Когда кому-то требуется актуальное состояние дом дерева компонента — дёргается dom_tree. Если нужно именно монтирование/размонтирование, то достаточно подписаться на события DOMNodeInsertedIntoDocuments и DOMNodeRemovedFromDocument. Это довольно простой, но весьма мощный жизненный цикл.

                                            +3
                                            Посмотрел ваши примеры, Дмитрий, теперь не смогу спать… Уже не раз зарекался, что не буду смотреть код на $mol, а просто ставить минус к комментарию с кодом, как это делают все, чтобы он быстрее стал сереньким, код стало не видно и он перестал давить на психику. Реально, после его прочтения снятся кошмары…
                                              –2
                                              И кошмары не прекратятся, пока вы не перестанете ёрничать, а разберётесь в синтаксисе. Так что не затягивайте, чтобы нам не пришлось вызывать вам санитаров.
                                                +1
                                                Да уж, если я начну разбираться в $mol, меня точно в дурку заберут. Видимо у меня не такая психика как у вас)))
                                            –5
                                            А разве React это фреймворк?
                                            Офф сайт говорит, что это библиотека.
                                              –3
                                              А самому подумать? Фреймворк — это то что на выходе дает готовое решение, которое Вы кастомизируете своими хуками/каллбэками/DI и прочей магией. Либа — это набор функций который не диктует Вам архитектуру приложения. Реакт — фреймворк, независимо от того что написано на сайте.
                                                +1
                                                Реакт разве вам что-то диктует?
                                                  –1
                                                  Да, конечно. Он ждет на входе отдельные компоненты из которых собирается готовое приложение. Компонента может быть функцией или классом. Вы не сможете разбивать свое приложение не на компоненты а на какую то другую, Вам более удобную абстракцию (выше или ниже компонент) и при этом остаться в Реакте. Реакт вам диктует гранулярность абстракции.
                                                    –1
                                                    Что именно вы не можете? Это же просто (пусть и кривой, огромный и неэффективный) шаблонизатор, вы его вольны использовать как вам вздумается.
                                                      0
                                                      Ну нет, шаблонизатор — это JSX сам по себе. Реакт же предполагает определенную логику синхронизации стейта.
                                                        –2
                                                        JSX — язык шаблонов. Реакт — сам шаблонизатор. Какие там у него внутри оптимизации — дело десятое. На вход ему передаётся шаблон всего приложения и стейт, на выходе — отрендеренное dom дерево.
                                                          +1
                                                          Реакт — сам шаблонизатор.

                                                          Нет. Если бы реакт был шаблонизатором — он бы только генерил dom из jsx. Он вместо этого генерит вдом, апдейтит его в соответствии с изменением пропсов, и апдейтит из него дом.


                                                          На вход ему передаётся шаблон всего приложения и стейт, на выходе — отрендеренное dom дерево.

                                                          Ну вот если бы это было все, что делает реакт — он был бы шаблонизатором. Но основная задача реакта — поддерживать сихнронность стейта и вида, что к работе шаблонизатора не относятся совсем никак. Шаблонизаторы не предполагают возможность навесить на баттон обработчик и заставить результат шаблона перегенериться. Можно писать на реакте вообще без jsx и это останется реактом, а можно писать с jsx но не на реакте. Самое близкое к шаблонизатору в этой системе — слой трансляции, отвечающий за преобразования jsx в js.

                                                  0
                                                  Во первых не хамите.
                                                  Во вторых Вы совершенно не понимаете, термины фреймворк и библиотека.

                                                  «Фреймворк» отличается от понятия библиотеки тем, что библиотека может быть использована в программном продукте просто как набор подпрограмм близкой функциональности, не влияя на архитектуру программного продукта и не накладывая на неё никаких ограничений. «Фреймворк» же диктует правила построения архитектуры приложения, задавая на начальном этапе разработки поведение по умолчанию — «каркас», который нужно будет расширять и изменять согласно указанным требованиям. Пример программного фреймворка — C.M.F. (Content Management Framework), а пример библиотеки — модуль электронной почты.

                                                  ru.wikipedia.org/wiki/%D0%A4%D1%80%D0%B5%D0%B9%D0%BC%D0%B2%D0%BE%D1%80%D0%BA#%D0%9E%D1%82%D0%BB%D0%B8%D1%87%D0%B8%D0%B5_%D0%BE%D1%82_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B8

                                                  В случае React это былобы React + Redux + React-Router + Blueprint +…
                                                  Так же как Rails = ActiveRecord + ActiveSupport + ActiveMailer +…

                                                  А так это библиотека для "building user interfaces"
                                                    0
                                                    Вы:
                                                    >то библиотека может быть… не влияя на архитектуру программного продукта и не накладывая на неё никаких ограничений.

                                                    Я:
                                                    >Либа — это набор функций который не диктует Вам архитектуру приложения.

                                                    Вопрос — это я не не понимаю терминологии или Вы не в состоянии вдуматься в то что цитируете? И да, можете считать что я хамлю.
                                                      0
                                                      React можно встроить в существующее приложение, даже если уже используется jquery, angular или backbone, он (react) не диктует правила построения архитектуры.

                                                      Соответственно он полностью подходит под определение библиотеки, а не фреймворка.

                                                      Проводите логический анализ текста перед тем как писать.
                                                        –1
                                                        реакт это чистое вью. И все что за пределами вью — да, он не диктует ничего. Прикручивайте редакс, роутер, граф апи, проблем нет. Но редакс диктует вам как будет организована логика вью. Он заставляет вас знать, когда будет перерисовка, обновление пропсов, у него есть свой лайфсайкл и вы его не можете изменить, можете только в него встроиться. И вот в переделах этого процесса редакс — фреймворк, за пределами его — называйте как хотите.
                                                        а теперь расскажите мне как вы будете встраивать в приложение где уже есть логика вью, не меняя этой логики. Например кусок кода jquery, который делает выборку по странице и навешивает на выбранное стили (или хандлеры или еще что то, пофигу) И тут у нас реакт, у которого перерисовка по каждому чиху. Будут они безконфликто существовать вместе? Не вынудит использование реакта засунуть jquery код внутрь компоненты? Не придется Вам, засовывая этот код в компоненту начать считаться со всеми правилами реакта и перебить код под эти правила?
                                                        Если у Вас есть такой реакт, в котором все это возможно — дайте ссылку, я с удовольствием с ним познакомлюсь.

                                                        ЗЫ — теоретики (небрежно)
                                                        0
                                                        Хотя конечно существует вероятность, что Вы умней и знаете больше об этой замечательной библиотеке, чем ее создатели. Но она крайне мизерна.
                                                  +1

                                                  Как то всё это грустно т.к. на Svelte всё гораздо проще получается, весит меньше а уж рессурсов жрёт куда меньше (он же магически исчезающий).
                                                  Мне кажется новый и современный фреймворк без компиляции и с VDOM это прошлый век.

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

                                                  Самое читаемое