Современный фронтенд шагнул далеко вперед со времен jQuery и обычных HTML страничек. У нас появились сборщики, менеджеры пакетов, компонентный подход, SPA, SSR и много еще чего.


Кажется, что у нас есть все, что нужно для счастья. Но индустрия двигается вперед. Я хочу вам рассказать о компилируемом фреймворке Svelte, и какие преимущества у него есть перед аналогами.



Автор изображения Andrew Walpole



Для понимания текущей ситуации во фронтенде я проанализировал популярность запросов в Google Trends по ключевым словам React, jQuery, Vue, Angular в России за последние 5 лет.



Google Trends


За последние 5 лет количество запросов, связанных с jQuery значительно сократилось, уступив место компонентным фреймворкам. Хотя jQuery и сдал позиции, он до сих пор остается популярным средством разработки.


Из этого графика можно сделать вывод, что компонентные библиотеки победили во фронтенде, а лидером в России является React.


Рынок труда


Мы пишем код не только для себя, но и за деньги. В основном за деньги. Поэтому рассматривать популярность фреймворков в отрыве от рынка труда глупо.


По количеству вакансий на hh первое место занимает React, за ним следует jQuery и другие компонентные библиотеки. Если мы посмотрим на количество соискателей, которые указали в ключевых навыках рассматриваемые библиотеки, то jQuery знают в 5 раз больше соискателей, чем React. И в 15 раз больше, чем Angular.



Рынок труда


Из этого графика можно сделать следующие выводы:


  • Компонентные фреймворки являются самыми востребованными среди работодателей, наиболее популярный среди них React.
  • Среди соискателей самой распространенной библиотекой является jQuery.

Итак, компонентные фреймворки победили. Фронтенд решил проблемы, которые стояли перед разработчиками во времена jQuery. Но новые подходы порождают новые неприятности. Какие проблемы вижу я?


  1. Производительность.
    В январе этого года Google анонсировал возможность публикации PWA приложений в google play, открыв дорогу javascript в магазин нативных приложений. Это накладывает определенную ответственность на разработчиков, ведь пользователи ожидают производительность нативных приложений, для потребителя не должно быть разницы.
    Еще Javascript покоряет low powered devices. Это смарт TV, часы, IoT. На таких устройствах ограниченный бюджет памяти и процессора, поэтому разработчики не могут себе позволить расточительно обращаться с ресурсами пользователя.
    У нас на работе есть опыт запуска React приложения на интернет хабе. Вышло так себе.
  2. Высокий порог входа.
    Как мы видели выше, большинство соискателей указывают в навыках jQuery, а не react. Освоить концепции React гораздо сложнее, чем подключить на страницу jQuery и начать творить.
  3. Зависимость от фреймворка.
    Если у вас есть библиотека компонентов, написанная на React, вы вряд ли сможете ее переиспользовать в проекте на Vue или Angular. Вы становитесь заложником экосистемы.

Svelte. Vanilla flavored.


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


Поскольку React является самой популярной библиотекой на территории России, дальнейшие примеры будут на React.


1. Производительность


Если вы начинаете знакомство с новой библиотекой, то скорее всего начнете тур с ToDo листа. Это достаточно простая задача, которую, зачастую, проще написать на ваниле. Если вы хотите углубиться во фреймворк, то отличным выбором будет обзор Real World Application. Это блог, который, по сути, является клоном Medium. Здесь есть регистрация, авторизация, создание постов, комментирование, лайки. Специалисты по фреймворку пишут реализацию функционала и добавляют в коллекцию Real World Application.
На FreeCodeCamp вышла статья о сравнении Real World Application, написанных на разных фреймворках.
Если мы посмотрим на размер итогового бандла, то Svelte выигрывает у конкурентов. Клиенту отправляется всего лишь 9.7кб кода. Как результат, это меньше времени на передачу данных, парсинг и обработку вашего кода.



Сравнение размера бандла Real World Application


А еще самый лучший код — это не написанный код.
Если мы посмотрим на количество строк кода, которые необходимы для написания функционала приложения, то на Svelte потребуется около 1 000 строк, а на React около 2 000. Чем меньше кода в вашем приложении, тем меньше в нем багов и проще поддержка.



Сравнение размера объема кода Real World Application


Давайте посмотрим на производительность. js-framework-benchmark предлагает сравнение производительности рендеринга среди фронтенд фреймворков. Тест заключается в отрисовке таблицы с большим количеством строк. Далее производятся манипуляции с этой таблицей: частичное или полное обновление, создание, очистка, удаление строк.


По времени обновления Svelte показывает лучшее, либо сопоставимое время. Svelte очень сбалансирован, нет перекосов при выполнении разных типов операций.



Сравнение времени выполнения обновления, мс


Если мы посмотрим на объем потребляемой памяти, то Svelte является наименее прожорливым среди рассматриваемых библиотек.



Сравнение объема потребляемой памяти, мб


Я не привык верить на слово и решил проверить все сам. Я нашел реализацию бенчмарка DBMonster для фронтенда и переписал реализацию на React 16.8 и Svelte 3. Тест заключается в рендеринге таблицы и последующим обновлении строк.



Как выглядит тест DBMonster


В ходе теста Svelte потреблял на 10мб памяти меньше и производил обновления на 10 мс быстрее, чем React.



Svelte / React


Приведенные тесты являются синтетическими, но из них можно сделать вывод, что при разработке на Svelte из коробки вы получите:


  • Меньший размер бандла
  • Меньшее потребление памяти
  • Более быстрые отрисовки.

2. Высокий порог входа


Если мы посмотрим на самый простой компонент на React, то вам потребуется импортировать сам React, написать функцию, которая вернет разметку и экспортировать ваш компонент. Итого 3 строчки кода.


import React from 'react';
const Component = () => (<div>Hello</div>);
export default Component;

Если мы посмотрим на самый простой пример компонента на svelte, то вы просто пишете разметку. Итого 1 строка кода.


<div>Hello</div>

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


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


Хочу поделиться примером с собеседования на позицию middle react developer.


setFilter() {
  this.switchFlag = !this.switchFlag
}
...
<button onClick={setFilter}>Filter</button>

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


Давайте разберем пример кнопки, которая по клику увеличивает счетчик.



На React вам потребуется переменная для хранения состояния и функция, которая умеет обновлять состояние. Далее на саму кнопку нужно назначить обработчик для обновления. Итого у меня получилось 8 строк кода.


import React from 'react';
const Component = () => {
    const [count, setCount] = React.useState(0)
    return <button onClick={() => setCount(count + 1)}>
        Clicked {count}
    </button>
}
export default Component;

Для решения аналогичной задачи на Svelte вам потребуется переменная для хранения состояния. Далее в обработчике вы просто изменяете значение этой переменной. Итого 6 строк кода.


<script>
let count = 0;
</script>
<button on:click={()=>count+=1}>
    Clicked {count}
</button>

Немного усложним пример. Допустим, нам нужно поле ввода, которое рядом выводит свое состояние



На React нам все так же потребуется переменная и функция для обновления состояния. Затем в поле ввода необходимо передать текущее значение и назначить обработчик на изменения. У меня в итоге получилось 11 строк кода.


import React from 'react';
const App = () => {
  const [value, setValue] = React.useState('');
  return (
    <React.Fragment>
      <input value={value} onChange={e => setValue(e.target.value)} />
      {value}
    </React.Fragment>
  );
}
export default App;

Для решения этой задачи на Svelte вам потребуется переменная, которая хранит состояние, а затем просто сделать двустороннюю привязку в поле ввода. Итого 5 строк кода.


<script>
    let value = '';
</script>
<input bind:value={value}/>
{value}

Если вам доводилось анимировать удаление элемента из DOM на React, то я вам сочувствую. На React потребуется либо враппер, который будет откладывать удаление элемента из DOM и производить анимацию, либо сам элемент остается в DOM, но анимация потребует управления свойством display или других манипуляций, чтобы элемент не занимал место.



Я пытался найти самую простую реализацию на React, в итоге получилось 35 строк кода. Если у вас есть решение проще, поделитесь в комментариях.


FadeIn FadeOut React
import React from "react";
import "./style.css";
const App = () => {
  const [visible, setVisible] = React.useState(true);
  return (
    <React.Fragment>
      <button onClick={() => setVisible(!visible)}>toggle</button>
      <div className={visible ? "visible" : "invisible"}>Hello</div>
    </React.Fragment>
  );
};
export default App;

.visible {
  animation: fadeIn 0.5s linear forwards;
}
.invisible {
  animation: fadeOut 0.5s linear forwards;
}
@keyframes fadeOut {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    display: none;
  }
}
@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

На Svelte аналогичный компонент требует всего 8 строк кода. В Svelte есть встроенный модуль для управления анимациями. Вы импортируете требуемый вид анимации, а затем говорите, как анимировать ваш компонент при добавлении и удалении.


<script>
    import { fade } from 'svelte/transition';
    let visible = true;
</script>
<button on:click={()=>visible=!visible}>toggle</button>
{#if visible}
    <div transition:fade>Hello</div>
{/if}

Компилируемость позволяет Svelte предоставлять разработчику крутые абстракции. И если вы их не используете, они не попадут в итоговый бандл.


Например, в модуле transition есть крутой функционал crossfade, который позволяет анимировать компонент при переходе из одного DOM узла в другой. С помощью него можно сделать такой переход задач в ToDo листе.



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



После знакомства со Svelte, мои друзья обычно говорят, что они не испытывали такого удовольствия от фронтенд разработки со времен jQuery.


3. Зависимость от фреймворка


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


Что предлагает Svelte? После компиляции ваш код превращается в обычный JS, который не требует рантайма. Это дает возможность использовать Svelte компонент в других фреймворках. Вам всего лишь потребуется один универсальный враппер. Например, адаптер для React и Vue svelte-adapter. Обернув компонент в адаптер, вы сможете использовать элемент, как обычный компонент.


import React from "react";
import SvelteSpinner from "svelte-spinner";
import toReact from "svelte-adapter/react";

const Spinner = toReact(SvelteSpinner, {}, "div");

const App = () => <Spinner size={50} />


Svelte поддерживает компиляцию в custom element, что еще больше расширяет границы применения компонентов. Посмотреть поддержку custom element различными фреймворками можно на custom-elements-everywhere.


Личный опыт


На работе писать на Svelte я пока не могу, поскольку мы плотно сидим на экосистеме React, но у меня есть личные проекты.
Ранее я писал, как опубликовал свое приложение Metalz в Google Play.



По моим ощущениям, Svelte позволяет писать более лаконичный и понятный код, при этом предоставляя широкий инструментарий для упрощения реализации.


Минусы


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


Попробуйте Svelte на небольших проектах, уверен, вам понравится.

Комментарии (35)


  1. ddidwyll
    17.10.2019 12:42

    у Svelte мало статей, где можно найти лучшие практики

    На мой взгляд, там всё просто и официальной документации вполне достаточно.


    1. sanReal Автор
      17.10.2019 12:44

      У Svelte прекрасная документация, но не хватает информации по построению архитектуры. Куда вынести стор, как организовать папки, а можно ли занести scss и т.д.


      1. ddidwyll
        17.10.2019 17:22
        +1

        Зато есть шанс побыть у истоков, и когда/если svelte выстрелит снять сливки.


        1. SEOVirus
          17.10.2019 21:43

          Это о каких сливках речь?)


          1. ddidwyll
            18.10.2019 08:28

            Ну я вот делаю набор ui компонентов на svelte и ещё по мелочи всяких штук, для реакта или вью в этом мало смысла, а так есть шанс что этим будет пользоваться кто-то кроме меня, что приятно. Если появятся вакансии на svelte — уже будет опыт и проекты в портфолио. При этом я просто решаю свои потребности и решаю с удобством :)


    1. GerrAlt
      17.10.2019 23:46

      Опробовал данный фреймворк на маленьком приложении — списочек с фильтрами на чекбоксах, документации мне не хватило — не нашел в ней:
      1) что блоки script и style должны идти именно в такой последовательности как в примерах (впрямую нигде не нашел упоминаний, мне было не очевидно)
      2) что собственные компоненты должны называться с большой буквы, иначе они тихо не рендерятся
      3) дебаггер chrome умудряется попадать дважды в onMount, пчм — так и не понял
      4) что последовательность инициализации компонентов такая, что внутри onMount передаваемые проперти еще не инициализированы, приходится писать костыли


      Несмотря на все это фреймворк понравился, буду следить и пробовать еще.


      1. sanReal Автор
        17.10.2019 23:59
        +1

        1) Порядок блоков не важен. Пруф
        2) Линтер говорит об этом

        3)Можете рассказать про кейсы? Мне пока onMount не пригодились
        4) Инициализируются. Пруф. Или я не понял ваш кейс


        1. GerrAlt
          21.10.2019 00:30

          по части 4 — github.com/sveltejs/svelte/issues/3470
          по части 3 — я хотел предобработать переданные параметры (начать загрузку ресурса, если указан урл)
          по части 2 — у меня был нем как рыба, подозреваю дело в том что имя компонента было checkbox и он подумал что это новомодный html тэг
          по части 1 — похоже что вы правы, сейчас когда я поменял местами пункты все осталось рабочим, возможно у меня что-то не так было с rollup, но ошибок при сборке не было, а при использовании последовательности из примеров все чинилось и работало


      1. PaulMaly
        18.10.2019 00:13

        Скорее всего вы были просто не очень внимательны:

        1) All three sections — script, styles and markup — are optional.. Очевидно в этом случае последовательность не может быть важна.
        2) A capitalised tag, such as Widget or Namespace.Widget, indicates a component.

        3, 4) Скорее всего вы ошиблись, хотя если сможете воспроизвести такое поведение в REPL будем благодарны за репорт.


        1. GerrAlt
          21.10.2019 00:22
          +1

          По части 4 репорт уже есть — github.com/sveltejs/svelte/issues/3470, по части 3 если восстановлю версию когда пытался использовать onMount обязательно напишу

          1) All three sections — script, styles and markup — are optional… Очевидно в этом случае последовательность не может быть важна.

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


  1. apapacy
    17.10.2019 12:55

    Не совсем ясно почему "кибернетически"
    Я люблю изучать новые фреймворки. Хотя до svelte пока не дошел но например работал с riot (и даже сделал реализацию realworld-app)
    Но честно говоря после выхода очередного нового лучшего чем все предыдущие уже хочется спросить "Шо, опять?". Я не уверен что между подходами всех перечисленных Вами фреймворков есть принципиально существенная разница, как например между jquery и всеми этими фреймворками. Мне кажется что дольше должна быть какая-то принципиально новая идея, которая позволить делать быстрее и лучше.


    1. Sybe
      18.10.2019 00:13
      +1

      Rich Harris в issue на гитхабе упоминал, почему был выбран именно такой слоган: github.com/sveltejs/svelte/issues/3269#issuecomment-516397747

      Someone: «Cybernatically» sounds 100% like 80s RoboCop corn
      Rich Harris: Exactly our goal! JavaScript tools are boooooring. We shouldn't take ourselves so seriously.

      Насчёт разницы между фреймворками — смотря с какой стороны посмотреть, например, Svelte — компилятор, а React/Vue — предоставляют рантайм, который занимается перерисовкой, в этом плане, отличие довольно принципиальное


  1. Akuma
    17.10.2019 13:04

    Сделайте роутинг, SSR, удобную синхронизацию данных с бекендом и будет довольно круто.

    Я для себя сейчас выбрал DerbyJS. Он довольно стар и непопулярен, зато все работает и очень удобно.


    1. sanReal Автор
      17.10.2019 13:08

      Для роутинга и SSR есть https://sapper.svelte.dev/


  1. inoyakaigor
    17.10.2019 15:34

    Svelte рано куда либо дальше песочницы на поиграться. Простой пример:

    <script>
    	let arr = []
    	
    	function add() {
    		arr.push(1)
    	}
    </script>
    
    <h1>Array length is {arr.length}</h1>
    
    <button on:click={add}>
    	add
    </button>

    Тут у нас всегда будет надпись Array length is 0

    Если функцию add() переписать вот так, то всё работает, но так писать не комильфо
    function add() {
    	arr = [...arr, 1]
    }
    


    UPD: ЧСХ в доках точно такой же пример тоже не работает тыц


    1. sanReal Автор
      17.10.2019 15:47

      Особенности Svelte. Если вам принципиально наличие push, то можно сделать так


      <script>
          import {writable} from 'svelte/store'
          const arr = writable([])
          function add() {
              arr.update(state=>{
                  state.push(1)
                  return state
              })
          }
      </script>
      
      <h1>Array length is {$arr.length}</h1>
      
      <button on:click={add}>
          add
      </button>


    1. YNile
      17.10.2019 15:49

      UPD: ЧСХ в доках точно такой же пример тоже не работает тыц

      Доки работают по принципу «попробуй сделать сам, а если нет — есть кнопка Show me, которая все сделает за тебя».
      monosnap.com/direct/C0g0VfTT2Of4jr6GReCcHGR79RI0NO


      1. inoyakaigor
        17.10.2019 16:04

        Ладно, согласен тут я не углядел, но сам факт того, что вместо привычного push придётся городить костыли удручает. Получается какая-то реактивность не до конца.


    1. PaulMaly
      17.10.2019 20:36

      Блин, вот незадача, а в React например не работает вот так:

      this.pushState({ arr: 1 });
      

      Представляете? Безобразие я считаю, похоже реакт не стоит использовать дальше песочницы. Изучать доки и апи, я конечно же не хочу. Просто хочу чтобы работало так, как мне кажется должно работать.


      1. justboris
        17.10.2019 22:36

        Можно на это посмотреть и с другой стороны:


        В React есть API документация в которой подробно расписано, что есть и чего нет.


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


        1. AlexxNB
          17.10.2019 22:48

          Там вполне достаточно и понятно написано, почему нельзя использовать push, и что нужно делать https://ru.svelte.dev/docs#2_Prisvaivaniya_reaktivny. Документация действительно короткая, да и ту не читают.


          1. justboris
            17.10.2019 22:49

            Если комменты подобные этому возникают под каждой статьей о Svelte, то, видимо, недостаточно подробно.


            1. AlexxNB
              18.10.2019 08:49

              Так проблема то не в том, то плохо написано, а в том, что "мужики инструкции не читают".
              Там всего три абзаца в этом разделе и один из абзацев конкретно про push и splice.


        1. PaulMaly
          17.10.2019 23:01

          В SvelteScript всего 4 особенности по сравнению с Javascript и они очень хорошо описаны в документации:

          1. export creates a component prop
          2. Assignments are 'reactive'
          3. $: marks a statement as reactive
          4. Prefix stores with $ to access their values

          Уверен, что абсолютное большенство программистов способно запомнить эти 4 пункта. Другое дело, люди не хотят читать документацию того, чем пользуются. Что подтверждается статьями вроде Боль и слёзы в Svelte 3.

          Ну и кажется, что все программисты должны понимать что такое assignments в JS. В этом смысле assignments это и есть апи, такое же как this.setState в React.


    1. staticlab
      18.10.2019 01:14

      Assignments are 'reactive'

      function add() {
        arr.push(1)
        arr = arr
      }

      «Бесполезное» присваивание будет скомпилировано в такую команду:


      $$invalidate('arr', arr);


      1. PaulMaly
        18.10.2019 01:50

        Оно не бесполезное, потому что апи Svelte подразумевает что только assignments are 'reactive'. Вы написали это самое присвоение и получили реактивность, не написали — не получили. Отслеживать мутации массива слишком дорого и совершенно не нужно. Более того, такой подход дает 2 плюса:

        1) склоняет к иммутабильности, а значит в более точному change detection на сложных типах
        2) в случае с массивом, например, позволяет синхронизировать его с отображением после нескольких мутаций (пару раз пригождалось на практике)

        arr.push(1); // не обновляет DOM
        arr.pop();// не обновляет DOM
        arr  = [ ...arr ]; // только когда мы явно этого захотели
        


        1. staticlab
          18.10.2019 10:19

          Я потому и написал в кавычках.


          1. PaulMaly
            18.10.2019 12:23

            А, я видимо не так интерпритировал смысл высказывания)


      1. AlexxNB
        18.10.2019 08:19

        Facebook агитировали за immutable.js. Смысл в немутируемости объектов:


        arr = arr.push(1);

        Да и в целом тренд на иммутабельность начался задолго до Svelte.


        1. staticlab
          18.10.2019 10:19

          Причём здесь вообще Фейсбук?


          1. AlexxNB
            18.10.2019 11:42

            Большая такая компания, отец Реакта, продвигает в массы иммутабельность. Можно прислушаться, как мне кажется.


            1. staticlab
              18.10.2019 12:13

              Я им не доверяю. Они изначально кричали, что JS быстрый, оптимизировать не нужно, а потом то PureComponent, то мемоизация компонентов, то байнд хендлеров, то useCallback. И кстати хайп вокруг immutable.js давным давно прошёл, да и медленная она.


              1. PaulMaly
                18.10.2019 12:28

                JS действительно быстрый за счет современных JIT, но его довольно легко можно сделать медленным. React не быстрый, но достаточно быстрый для задач Facebook. PureComponent/useMemo/useCallback — это костыли решающие проблемы только React и VDOM. Но иммутабильность — это средство решения проблем JS в целом, и даже можно сказать обще-языковых. Так как реальным значением переменной сложного типа является ссылка, понять что данная переменная изменилась можно только изменив значение, то есть эту ссылку. Определение какие именно мутации произошли внутри сложного типа крайне затратная штука, поэтому проще изменить значение и использовать строго стравнение. А этом смысл иммутабильности, а immutable.js это не более чем один из инструментов работы с ней. Лично мне он не нравится ни разу, но он в свое время был хорошо распиарен тем же Facebook.


  1. Agator
    21.10.2019 08:34

    На официальном сайте топовые примеры. Пишешь 11 строчек кода, после преобразований становится 78. Как мне оптимизировать код, если он под капотом фреймворка меняется?

    Если сам фреймворк весит 9 кб, но зато код написанный на нём увеличивается в 7 раз в размерах, это не значит, что он лёгкий.

    И как то не логично сравнивать размеры сайтов, при условии что на SvelteJS написано крайне мало сайтов и скорее всего очень мелких, как ваш стартап.


  1. sanReal Автор
    21.10.2019 08:37

    Как мне оптимизировать код, если он под капотом фреймворка меняется?

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


    Если сам фреймворк весит 9 кб, но зато код написанный на нём увеличивается в 7 раз в размерах, это не значит, что он лёгкий.

    И все равно получается меньше, чем брать Реакт.