Хабрахабр

Исчезающие фреймворки

Почему мы используем фреймворки? Angular, React и Vue помогают нам строить сложные веб-приложения: структурировать код, управлять состоянием и разрабатывать сложные интерфейсы. За последнее десятилетие фреймворки стали стандартом. Дорогим стандартом. С тех пор как фреймворки вошли в обиход, JS сильно раздуло. Страниц весом несколько мегабайт становится больше, и существенная часть этого объема — фреймворк сам по себе. Кажется, это немного, но для пользователя критично — на телефоне или слабом интернете страница может даже не загрузиться. Это проблема.

Эти проблемы решены в «исчезающих фреймворках». Кроме лишнего веса у классических фреймворков много других минусов и они подходят не для всех проектов. Работает в компании Mustlab, которая занимается аутсорсингом веб, Smart TV и мобильных устройств, а также IoT. Что это за новый тренд, почему это не «еще один JS-фреймворк», а хорошая идея и как таинственные фреймворки могут улучшить наши приложения, расскажет Павел Малышев.
О спикере: Павел Малышев (PaulMaly) — fullstack со стажем больше 10 лет. Евангелист фреймворка Svelte JS в России, ведет Telegram-группу на эту тему.

Чего не должно быть в докладе

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

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

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

Для начала разберемся, кто есть кто.

Кто есть кто

Я взял большую тройку и Svelte. Интересно то, что каждый инструмент характеризует себя по-разному. Angular позиционирует себя, как фреймворк на все случаи жизни, «все в коробке». React мимикрирует под библиотеку: «Я такой маленький, как библиотека», но вместе со всей инфраструктурой это тоже полноценный фреймворк. Vue — якобы прогрессивный фреймворк. Вы можете переходить на него с jQuery последовательно перенося код, начиная чуть ли не с обычного тэга script без сборщиков. Svelte — магически исчезающий фреймворк. WAT?

Давайте разбираться, о чем это я вообще.

Почему фреймворки стали исчезать?

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

Наши клиенты становятся более мобильными, но технологии интернета не везде хороши — даже 3G есть не везде, не говоря про LTE. Нам надо сокращать размер кода.

Если JS мало, то до 170Kb. На эту тему есть интересное исследование от 2017 года, которое показывает, что если в ваше приложении много JS, то вы можете поставить лишь 130 Kb, чтобы пользователь был доволен. Такая разница обусловлена тем, что в исследовании учитывалось, что JS отличается от других видов ресурсов, его надо распарсить, интерпретировать и прочее.

При этом один Angular весит 143 Kb, а мы еще даже не написали ни одной строчки кода. В исследовании 130 Kb — это не только JS, а вообще все, включая CSS, HTML, конечно, без картинок. Посмотрим, что нам предлагают текущие решения.

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

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

Мы несем дополнительные издержки из-за лишних абстракций между нашим кодом и браузером.

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

Совместимость

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

Они работают только в рамках этого фреймворка. Например, вы делаете 2-3 проекта на своем любимом React, написали для него кучу классных решений, а потом решили переехать на Angular. Эти решения вам больше не нужны, их можно выкинуть. Хотя основная идея — соответствие компонентов, классические фреймворки лишают нас возможности переиспользования кода.

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

Code splitting & tree shaking

Старые технологии мешают применять новые технологии.

Большинство фреймворков создавались когда эти технологии не так были развиты. Мы имеем достаточно монолитное ядро, и любой компонент, даже минимальный кусочек вашего кода завязан на весь фреймворк. Соответственно, эффективно делать code splitting и даже tree shaking не получается.

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

Feature cost

Любой фреймворк имеет цену новой фичи.

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

Безс рантайма

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

Runtime как окружение

Разберемся на примере. Если код написан на Vanilla, то единственный рантайм, который нужен — это DOM. Если код написан на React, то нужен другой рантайм: DOM, React и React DOM.

Любой, самый маленький компонент вашего кода в React будет зависеть от того, в каком окружении работает ваш фреймворк, то есть от рантайма.

Runtime как исполнение кода

Второй момент — рантайм с точки зрения исполнения кода во время работы. Рассмотрим базовый на сегодня кейс рантайма для фронтенда — виртуальный DOM — большой кусок рантайма, например, в React-приложениях, — на простом примере:

<h1>Hello </h1>

В этом куске HTML есть динамическая переменная. С точки зрения виртуального DOM код работает так.

  • Происходит изменение этой переменной.
  • Строится дерево виртуального DOM в памяти.
  • Сравнивается с предыдущей версией.
  • Выполняются непосредственно DOM-манипуляции, которые в итоге довольно эффективные, потому что мы получили некий diff и знаем точно, что где поменять в реальном DOM.

Виртуальный DOM — это абсолютный overhead, потому что всегда будет быстрее так: как только произошли изменения, сразу сделали изменения в DOM.

Виртуальный DOM решает вопрос того, что изменилось, а не скорости этих изменений. Вопрос: как понять, что изменилось и что нужно изменить? Скорость достигается за счет того, что мы максимально хорошо знаем, что изменилось, и можем эффективно манипулировать с DOM.

Скомпилируй это

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

But a compiler can do it for you.
Rich Harris, создатель Svelte You can’t write serious applications in vanilla JavaScript without hitting a complexity wall.

Расскажу, как я познакомился со Svelte. У нас был интересный проект — виджет для вставки на неограниченное количество сайтов, при этом мы не знаем, какие это сайты, на каких устройствах пользователь будет их открывать. Виджет должен быть очень маленьким по размеру и очень быстрым по скорости. Естественно, когда мы взялись за эту задачу, сразу поняли, что делать на React или даже на Vue не вариант, потому что получается достаточно много лишних накладных расходов.

Но нам повезло — мы наткнулись на Svelte. Мы практически склонились написать на Vanilla, но это сложно и не всегда удобно поддерживать. Основная идея этого фреймворка приведена в цитате: вы не можете написать серьезное приложение на Vanilla, потому что очень быстро упретесь в стену сложности — команде будет сложно работать, нет общих подходов, а компилятор может сделать это за вас.

Как это работает

Все как обычно: есть некая документация, вы пишите код, как будто бы вы используете любой другой фреймворк, в котором есть какие-то API, принципы и подходы. Исходный код — это framework-based code — код, который зависит от фреймворка. Все как обычно.

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

Первый шаг — component. Рассмотрим этапы поподробнее. Рассмотрим тот же компонент.

<h1>Hello {world}</h1>

На втором шаге — AST — код продуцируется в абстрактное синтаксическое дерево.

{"type": "Element", "name": "h1", "children": [{ "type": "Text", "data": "Hello",....}]}

Третий шаг — DOM API. На основе дерева производится непосредственно генерация кода, который необходим для работы этого компонента. Код очень простой, никаких сложных рантаймов и абстракций здесь нет в принципе.

function update(changed, ctx) { if (changed.name) { text_1.data = ctx.name; }
}

В конце статьи укажите в опросе, нравится ли вам идея AOT-компиляции фреймворка Svelte? В прошлый раз большинство высказалось положительно.

Теперь давайте посмотрим, что такое Svelte.

Svelte

Английское слово svelte или svelt, означает «стройный, гибкий». Svelte JS такой и есть!

Обычные фреймворки работают в рантайме, а Svelte существует только до того, как мы его скомпилировали. Я утверждаю, что это не «Yet another JS framework», потому что это build-time UI framework.

Это compile-time static analyzer, или статический анализатор кода, который во время компиляции проходит по коду, статически его анализирует и на основе этого генерирует соответствующий итоговый бандл.

Это ahead-of-time compiler — AOT-компилятор, который это все делает.

На мой взгляд, тот же самый Rollup до сих пор остается лучшей реализацией tree-shaking. Создатель фреймворка Rich Harris, которого вы можете знать по таким инструментам, как Rollup, Ractive, Buble — аналога Babel — крутой чувак. Harris изначально делал фреймворк Svelte с учетом tree-shaking, code splitting и других современных подходов. В нем он появился гораздо раньше, чем в Webpack, и до сих пор работает лучше.

Svelte — это средство, писать vanilla JS без необходимости писать на vanilla JS.

Давайте посмотрим на этот фреймворк изнутри.

Однофайловый компонент

Svelte — однофайловый компонент. Если вы работали с Vue, он вам будет очень напоминать этот фреймворк, но это неспроста. Синтаксис, который преимущественно используется в Vue, позаимствован из фреймворка Ractive, который тоже сделал Harris еще в 2012 году. Этот фреймворк, к сожалению, не стал популярным, мне кажется, потому что вышел в 1.0 слишком поздно. Они так замутили с версиями, что Ractive 6 лет был в минорных версиях.

Поэтому вам будет казаться, что все похоже, но однофайловые компоненты появились в Ractive в 2013 году. Многие интерфейсы Vue оттуда позаимствовал, а Svelte — продолжатель Ractive. Любой компонент, который вы будете писать на Svelte, будет выглядеть примерно так. Они выглядят чуть-чуть по-другому, но в целом — похоже.

<h1 >Hello {world}!</h1 >
<script> export default { /* component behaviour */ };
</script>
<style> /* scoped styles */ </style>

Сверху некий HTML-шаблон — обычный HTML без каких-то исключений. Единственное, что на него сверху наворочен некий язык шаблонов, который называется HTMLx. Про него я расскажу подробней позже.

Как его писать, описано в гайде. Дальше идет скрипт, в котором мы пишем поведение нашего компонента. Там можно импортировать, делать разные функции и методы — все как во Vue.

Это работает так: в main-компиляции генерируется хэш, в итоге получается как бы эмуляция Shadow DOM. Можно использовать scoped style, чтобы получить из коробки изолированные стили компонентов.

Ни одна из секций не обязательна, поэтому компонент может состоять только из одного HTML.

Синтаксис HTMLx

Как и в любом HTML, в скобках записываются данные.

<button>Say hello {name}</button>

Так записываются условные операторы.

{#if name} <button>Say hello {name}</button> {/if}

Перечисления и циклы:

{#each users as { name, email}} {#if name} <button>Say hello {name}</button> {/if}
{/each}

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

{#each users as { name, email}} {#if name} <button on:click="say(name)">Say hello {name}<button> {/if}
{/each}

В HTMLx очень мало директив — всего 4-5 видов, а кастомных директив, как в Angular, вообще нет.

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

<div id= "{dynamic}"class="used">
{#each users as { name, email}} {#if name} <button on:click="say(name) " >Say hello {name}<button> {/if}
{/each}
</div>

Изолированные стили и удаление неиспользованных

Если вы используете изолированные стили, то кроме того, что они изолируются, неиспользуемые стили еще вырезаются автоматически.

<div id= "{dynamic}"class="used">
{#each users as { name, email}} {#if name} <button on:click="say(name) " >Say hello {name}<button> {/if}
{/each}
</div>
</style> .used {...} .unused {...}
</style>

Если стиль used, то он используется, а если стиль unused — его не будет в бандле. Фреймворк все порежет, но аккуратно. Поэтому эту фичу можно легко сломать, если использовать много динамики. Svelte подумает: «Я лучше не буду трогать» — и оставит. Надо быть аккуратным, но это очень удобно. Так как в Svelte есть статический анализатор, он показывает в консоли при запуске, какие стили не используются. Если они вам действительно не нужны, их можно сразу же убрать.

Композиция компонентов

Здесь все как обычно: импортируем файл, регистрируем его в компонентах, используем как тэг.

<Nested foo= "static" bar={dynamic} bind:baz=baz /> <script> import Nested from ‘Nested.html’;’ export default { components: {Nested} };
</script>

Можно через атрибуты передавать любые данные, использовать двойное связывание. Или не использовать — двойное связывание не обязательное, его можно даже отключить на уровне компилятора, и его вообще не будет нигде. Это для тех, кто не любит биндинги. Биндинги получают с помощью директивы bind:.

Дополнительные возможности Svelte

Первые две возможности: SSR & Hydration — серверный рендеринг и гидрация, и built-in state management — встроенное глобальное управление состоянием на основе собственного решения, так называемы store.

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

Если у вас, например, есть Root-компонент, он используется во всей этой иерархии. У store есть интересная особенность, которую я больше нигде не видел, — он распространяется глобально на одну иерархию компонентов. Если у вас появился другой store где-то внутри этой иерархии, то на всю нижнюю иерархию распространяется уже этот store.

Если вы используете обычные store с Redux, то вы должны убедиться, что в проекте, куда вы его добавляете, есть Redux. Это удобно когда вы хотите сделать большой сложный компонент со множеством подкомпонентов, и дальше его использовать в разных проектах, в одних из которых есть Redux, а в других нет. Его можно легко переносить даже в те проекты, где глобального store нет вообще. Здесь это не нужно делать — Svelte реализует свой собственный store для этой подиерархии. Вы занесли, а Svelte внутри себя уже все делает.

Есть специальный сайт на котором тестируется поддержка фреймворками стандарта Web Components. Custom elements поддерживаются из коробки. Это работает так: указывается имя тэга, в компиляторе ставится флажок «customElement: true», и генерируется готовый к использованию кастомный элемент, который поддерживает все существующие возможности Svelte. Согласно тестам Svelte имеет 100% поддержку кастомных элементов.

Это аналог Next.js и Nuxt.js для Vue. Sapper — Svelte app maker. По-моему, недавно даже часть идей роутинга вошла из Sapper в Next.js. В принципе все то же самое: изоморфность, поддержка code-splitting, prefetching и offline из коробки и другие фичи.

Мы за добросовестную конкуренцию! Но не один Svelte этим занимается.

Кто еще?

Stencil — это фреймворк от разработчиков Ionic. Они также работают над компилируемым фреймворком, но с большим уклоном именно на веб-компоненты и кастомные элементы. Svelte отличается от него, тем что его компоненты, как правило, принято компилировать в обычные JS классы и нет уклона в веб-компоненты.

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

Подробно не изучал, но они тоже компилируют, не уверен, что настолько глубоко, как Svelte. Marko от eBay. Правда я и не разбирался подробно и могу ошибаться.

Здесь есть AOT-компилятор, и он хорошо промоутится, но это просто заранее перекомпилирование строковых шаблонов в уже готовый разобранный вид. Angular. Насколько я понимаю, полной AoT-компиляции там нет пока.

Hello movies

Я написал небольшое приложение — список фильмов с фильтрами на основе TMDb API. В нем есть категории: popular, top rated, upcoming. Можно походить по страничкам, выбрать страну, применить для нее фильтры, поменять язык. Это примитивное приложение занимает всего 6,4 Kb.

Это весь код который я написал лично. В общей сложности HELLO MOVIES — это всего 172 строчки кода вместе с HTML, из них 36 строчек JS-кода вместе со всякими кавычками. Естественно, все это компилируется в гораздо больший объем, но для меня, как для разработчика, это неважно.

«TodoList MVC» на Svelte занимает 3,6 Kb — зависит от того, кто как пишет, но даже на Vanilla он занимает 11 Kb, а на Vue и React и того больше — по 80 и 300 Kb, соответственно. Еще немного показателей. Клон «HackerNews» на Svelte — 30 Kb, на React и Vue — в несколько раз больше. Проект «RealWorld», клон Medium, с регистрацией, аккаунтами, лайками, шэрами, френдами — 40 Kb, на React и Angular по 211 и 575 Kb.

Бенчмарки

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

В итоге выделил две первых позиции относительно Vanilla. Я взял сравнение Svelte с большой тройкой — Angular, React и Vue в последних версиях. Причем здесь цифры не так важны, как основная идея, которая заключается в том, что Svelte очень сбалансирован.

По некоторым параметрам Svelte даже обгоняет Vanilla, по некоторым занимает первое, а где-то его обгоняет Vue и Angular. Svelte стремится быть сбалансированным — он хорош по всем показателям в любых бенчмарках. Он не стремится быть самым лучшым вообще везде, хотя он сопоставимо быстрый, потребляет мало памяти и имеет быстрый запуск. При этом во всех тестах Angular и Vue потребляют много памяти, а Svelte — нет. Основная суть — баланс.

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

Чик-чик — и в продакшн!

Автор Svelte работает в New York Times. Это достаточно большой продакшн. Конечно, не весь сайт сделан на Svelte, думаю, это большой legacy-проект с CMS, где все сделано прежде всего для редакторов. Многие интерактивные спецпроекты и виджеты, которые они помещают на странице, сделаны на этом фреймворке.

Этот виджет реализован как раз на Svelte, и таких примеров много. Например, интерактивный виджет-опрос по теме «Игры престолов» — с анимашками, с Drag&Drop.

Я это знаю, потому что ее разработчик сидит у нас в Телеграм-чатике и мы с ним общаемся. Японская облачная платформа для игр Soft-Gear с достаточно сложной админкой, у которой я видел скриншоты, на Svelte.

Редактор сервиса сторителлинга Dabble полностью на Svelte. Интерфейс сервиса мультикастинга airca.st полностью сделан на Svelte. Забавный сервис поиска электро-велосипедов поблизости i-want-to-ride-an-electric-citi с прикольными картами и анимашками тоже использует Svelte.

Наверное, все знают RuTube. В заключение еще один наш кейс. Их новое приложение для Tizen для Smart TV будет написано тоже на Svelte. Сейчас мы сделали им обновление для Smart TV для платформ Tizen, в том числе Samsung. Оно уже сейчас готовится к публикации, будет выглядеть примерно так.

Итого плюсы

Он решает только те проблемы, которые вы решаете в своих компонентах, никакого overhead нет. Итоговый код standalone vanilla JavaScript без зависимостей.

В Svelte нет никаких абстракций между кодом и DOM. Небольшой размер и высокая производительность. Здесь вообще минимум абстракций.

Все говорят, что очень просто обучиться Vue, но изучить Svelte еще проще. Низкий порог входа. У него простой гайд, отличный REPL, причем все ссылки с гайда идут на готовый пример, который можно потыкать и изменять.

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

Опять же на уровне концепции решены проблемы с code-splitting и tree-shaking. Code-splitting & tree-shaking. Это будут отдельные независимые модули, которые можно сочетать в любой последовательности и получать разные пакеты. Все компоненты и любую иерархию компоненты можно собрать отдельно.

Это очень хорошо для микро-фронтендов. Micro-frontends ready. притом, что общая кодовая база была единая. У нас даже есть небольшой опыт, когда на каждой странице, которая отдавалась с сервера с помощью обычного PHP, находилось полностью независимое Svelte-приложение. Даже внутри этих приложений можно независимо проводить code-splitting и tree-shaking. Мы просто набирали из разных компонентов, для каждой страницы собирали отдельно и получали абсолютно независимые приложения.

Итого минусы

Первые релизы были в конце 2016 года. Svelte довольно молод, мало готовых решений. Но уже есть вторая версия и даже идут обсуждения третьей.

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

Если React поддерживает Facebook, Angular — Google, Vue кто только не поддерживает, то здесь проект держится на Rich Harris и команде майнтейнеров, которые вместе с ним работают и я рад быть частью этой команды. Нет поддержки корпораций.

И еще 33 отличных доклада по фронтенду будет на фестивале. Всего через неделю на Frontend Conf в рамках РИТ++ Павел Малышев пойдет дальше и расскажет, что делать, если хочется больше ванилы. Читайте здесь подробнее о темах и концепции и успевайте присоединиться к большой компании профессионалов на РИТ++ 27 и 28 мая.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть