React — всё? Стоит ли переходить на Svelte и SolidJS

А вы тоже в последнее время всё чаще слышите о Svelte и SolidJS? В этой статье рассмотрим, какие у них преимущества перед React, разберемся, для каких типов проектов подходят лучше классических React-решений и стоит ли вообще переходить на них как на основной инструмент разработки.

93 открытий300 показов
React — всё? Стоит ли переходить на Svelte и SolidJS

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

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

React решил эти проблемы. Разработчики получили виртуальный DOM, который ускорял отрисовку веб-страниц. Благодаря компонентам стало удобнее верстать сайты и веб-приложения, а реактивность помогла эффективнее внедрять интерактивные элементы. Да, рeact действительно крутой, однако он не лишён недостатков в сравнении с некоторыми более новыми фреймворками.

При написании статьи консультировался с Денисом Черновым, ведущим разработчиком платформенной команды Спортмастера (а то я чуть не убил Реакт), а также Мишей Родштейном, руководителем разработки в Дизайн-бюро «Интуиция», и Александром Гузенко, руководителем фронтенд-разработки отдела R&D в крупной европейской компании (к сожалению, под NDA).

Почему разработчики ищут замену React?

Спорный JSX

В обзорных статьях и видео React часто критикуют за JSX код. JSX — это плод любви HTML и JS, который выглядит как HTML, но таковым не является. У нас в JSX коде смешивается логика и верстка. В итоге получаем кашу.

Пример JSX кода:

			<ul>
  {items.map((item, index) => (
    <li key={index}>{item}</li>
  ))}
</ul>

		

Такой код немного хуже читается, чем обычные решения на HTML + JS, и может сбивать с толку новичков. Но стоит сделать ремарку, что некоторым разработчикам он нравится.

Виртуальный DOM как устаревший подход к оптимизации

DOM — еще одна крутая фишка React, которая со временем стала недостатком на фоне других фреймворков. Когда пользователь взаимодействует с интерактивным элементом, на его стороне сперва рендерится виртуальный DOM, после чего он сравнивается с главным DOM и перезаписывает данные на сервере.

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

Зависимость от сторонних библиотек

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

В итоге мы получаем крутой инструмент, но с недостатками в виде не самого чистого кода, весомого бандла, зависимости от сторонних библиотек, трудностей в поддержке и не самой быстрой реализации рендеринга DOM.

Краткое знакомство с Svelte и SolidJS

Что такое Svelte: основные принципы, чем выделяется

Производительность из-за компиляции

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

Благодаря компиляции кода, проекты на Svelte получаются более производительными и меньше весят. Здесь не нужны костыли в виде виртуального DOM.

Отсутствие JSX-кода и модифицированный HTML

Код в Svelte пишется на HTML, CSS и JS, причем HTML здесь модифицированный. Например, при верстке мы можем использовать директивы:

  • bind: для двустороннего связывания данных (например, <input bind:value={name} />).
  • on: для добавления обработчиков событий (например, <button on:click={handleClick}>).
  • class: для условного добавления классов (например, <div class:active={isActive}>).

Вот пример простого кода — он создает интерактивный интерфейс, который реагирует на ввод пользователя и переключение состояния:

			<script>
  let name = 'world';
</script>

<input bind:value={name} placeholder="Enter your name">

<p>Hello, {name}!</p>

		

Значение в поле ввода связано с переменной name при помощи bind:value. Когда пользователь вводит текст, значение name обновляется, и это изменение отражается в приветствии ниже.

Инкапсуляция стилей

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

Например:

			<style>
    p {
        color: blue;
    }
</style>

<p>Этот параграф будет синим только в этом компоненте.</p>
<p>И этот тоже будет синим!</p>

		

Svelte при компиляции присваивает атрибутам внутри элемента идентификатор — так он реализует инкапсуляцию стилей.

Вот пример идентификатора для тега «p»:

			<p svelte-abc123>Этот параграф будет синим только в этом компоненте.</p>
		

А вот для стилей:

			p[svelte-abc123] {
    color: blue;
}

		

Если нам нужно, чтобы стиль был глобальным, то нужно использовать :global:

			:global(h1) {    color: red;}

		

Анимации из коробки

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

Здесь есть:

  • fade — плавное появление/исчезновение элемента за счет изменения прозрачности.
  • fly — плавное перемещение элемента с одного места в другое (можно указать направление и расстояние).
  • scale — изменение размера элемента, создаёт эффект увеличения или уменьшения.
  • slide — плавное скольжение элемента с одного края экрана в другой (например, слева или сверху).
  • draw — анимация, которая используется для рисования элементов, например, для SVG-изображений.
  • blur — плавное размытие элемента, создаёт эффект фокусировки/размытия.

Пример анимации с использованием fly:

			<script>
    import { fly } from 'svelte/transition';
    let visible = true;
</script>

{#if visible}
    <div transition:fly={{ x: 200 }}>
        <p>Элемент будет плавно перемещаться по оси X.</p>
    </div>
{/if}

<button on:click={() => visible = !visible}>
    {visible ? 'Скрыть' : 'Показать'}
</button>

		

В этом примере элемент будет плавно перемещаться по горизонтальной оси (ось X), когда он появляется на экране.

По умолчанию анимации в Svelte работают локально, внутри компонента, как и стили. Но их можно сделать глобальными при помощи директивы :global:

			<style>
    :global(p) {
        animation: blurEffect 3s ease-in-out;
    }

    @keyframes blurEffect {
        from {
            filter: blur(10px);
        }
        to {
            filter: blur(0);
        }
    }
</style>

<p>Этот текст будет плавно терять размытость.</p>

		

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

SolidJS: чем уникален, ключевые особенности

Компиляция вместо VDOM

SolidJS похож на React. Тут тоже есть компоненты, реактивность и JSX. Но в отличие от своего старшего собрата он не поддерживает VDOM. Это здесь просто не нужно по той же причине, что и в Svelte. SolidJS более эффективно компилирует проект в чистый JS-код. В итоге страница рендерится быстрее, чем при работе в React с его виртуальным DOM.

Гранулярная реактивность

Реактивность в SolidJS работает при помощи сигналов. Сигналы хранят в себе данные и отслеживают их изменения. Если данные меняются, то происходит рендер только тех участков компонентов, с которыми эти сигналы связаны. Сам компонент целиком не рендерится в DOM, только его часть.

Пример кода с сигналами:

			import { createSignal } from "solid-js";

function Counter() {
  // Создаем сигнал с начальными данными
  const [count, setCount] = createSignal(0);

  return (
    <div>
      {/* Используем сигнал для отображения значения */}
      <h1>Счетчик: {count()}</h1>
      {/* Кнопка, которая обновляет сигнал */}
      <button onClick={() => setCount(count() + 1)}>Увеличить</button>
    </div>
  );
}

export default Counter;

		

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

Реактивные хранилища

Реактивные хранилища напоминают сигналы, но на максималках. Если сигналы хранят и отслеживают единичные значения, то в хранилищах могут быть другие сигналы, массивы, объекты, обычные переменные, коллекции и состояния.

Пример создания реактивного хранилища:

			import { createStore } from "solid-js/store";

function UserProfile() {
  // Создаем хранилище для данных пользователя
  const [user, setUser] = createStore({
    name: "Иван",
    age: 30,
    settings: {
      theme: "light",
      notifications: true,
    },
  });

		

При помощи реактивных хранилищ нам будет удобнее управлять состоянием и синхронизацией данных в приложении.

Сравнение: Svelte, SolidJS и React

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

Проблемы реактивности в React

Реактивность в React реализована таким образом, что у нас обновляется не весь DOM, а только конкретный компонент через виртуальный DOM. В свое время это было прорывным решением. Если пользователь кликнул по кнопке, то у него изменялся не весь сайт, а только эта кнопка.

С выходом SolidJS и Svelte появились более производительные решения рендеринга интерактивных элементов. Всё благодаря компиляции кода без VDOM. Из-за такой реализации работы с DOM SolidJS и Svelte показывают большую производительность в бенчмарках.

Сравниваем производительность SolidJS, Svelte и React в бенчмарках

Для сравнения фреймворков я взял результаты тестов из бенчмарка js-framework-benchmark results для Chrome 130.0.6723.58. Использовались следующие версии фреймворков:

  • Solid 1.8.15
  • Svelte 5.0.5
  • React 18.2.0

Результаты тестов записаны в миллисекундах. В качестве режима отображения выбрано медианное значение. Когда мы видим запись 38.8 ± 0.2, она означает, что есть погрешность в 0.2 миллисекунды и разброс может быть от 38.6 до 39.

Общая производительность

React — всё? Стоит ли переходить на Svelte и SolidJS 1
Тесты производительности

Видно, что SolidJS — самый производительный из всей троицы. Следом идет Svelte с незначительным отставанием. React гораздо более медленный, в некоторых тестах он многократно уступает другим фреймворкам.

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

Работа с памятью
React — всё? Стоит ли переходить на Svelte и SolidJS 2
Тесты работы с памятью

React во всех случаях занимает память ощутимо больше, чем Svelte и SolidJS. Svelte незначительно, но уступает Solid.

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

Удобство разработки

Простота синтаксиса

Новичкам удобнее всего будет работать в Svelte. Там используется хоть и модифицированный, но более понятный HTML, CSS и JS. Благодаря этому код выглядит чище и проще.

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

Вот пример кода на Svelte, который создает простой счетчик:

			<script>
  import { state } from '@svelte/rune';

  // Создаем реактивное состояние для переменной count
  const count = state(0);
</script>

<button on:click={() => count.set(count.get() + 1)}>
  Count: {count.get()}
</button>

		

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

А вот реализация на JSX в React:

			import React, { useState } from 'react';

function Counter() {
  // Используем хук useState для управления состоянием
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

export default Counter;

		

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

Теперь сравним с реализацией в SolidJS:

			import { createSignal } from 'solid-js';

function Counter() {
  // Используем createSignal для реактивного состояния
  const [count, setCount] = createSignal(0);

  return (
    <button onClick={() => setCount(count() + 1)}>
      Count: {count()}
    </button>
  );
}

export default Counter;

		

Здесь реактивность работает из коробки, поэтому код выглядит чуть более удобным, чем в React. Но JSX синтаксис по-прежнему проигрывает в массивности и читаемости.

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

Менеджер состояний

В React управление состоянием традиционно строится на хуках, таких как useState и useReducer.

			import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

		

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

Переменные в компоненте Svelte до 5-ой версии автоматически становились реактивными. Но с пятой версии мы можем гибко управлять реактивностью при помощи специальных функций — рун.

Рассмотрим более подробно код из предыдущего примера со Svelte:

			<script>
  import { state } from '@svelte/rune';

  // Создаем реактивное состояние для переменной count
  const count = state(0);
</script>

<button on:click={() => count.set(count.get() + 1)}>
  Count: {count.get()}
</button>

		

Здесь мы используем руну state(). Она хранит данные, которые автоматически обновляют интерфейс.

SolidJS использует fine-grained reactivity, подобно Svelte, но с еще более детальным контролем. SolidJS отслеживает зависимости на уровне отдельных переменных, обновляя только те части DOM, которые зависят от изменения переменной.

В этом примере Solid перерисовывает только текст внутри кнопки, а не всю кнопку при изменении данных:

			import { createSignal } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);

  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>Increment</button>
    </div>
  );
}

		

SolidJS более тонко работает с состояниями и реактивностью, чем Svelte и React. Зато в последних есть возможность более гибко их настраивать.

Экосистема, документация и трудоустройство

Зрелость React против развивающихся экосистем

React гораздо популярнее, чем SolidJS и Svelte. По данным Statista, React используют 39.5% веб-разработчиков. По своей популярности он уступает только NodeJS с его 40.8% (судя по данным, опрашиваемые могли выбрать несколько вариантов ответов). Для сравнения у Svelte 6.5%, а у SolidJS — 1.2%.

На React кодят больше. Это значит, что под него проще найти учебные материалы и получить ответы на вопросы на форумах. А у работодателей — более широкий пул кандидатов.

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

Документация ко всем трём фреймворкам переведена на русский. В целом при подготовке статьи и сборе материалов чувствовалось, что о React есть много информации на русском, о Svelte меньше, а про SolidJS — в разы меньше.

Трудоустройство

На момент написания статьи React упоминают в 2669 вакансиях на hh.ru. Сотрудников без опыта рассматривают в 128 вакансиях. Кстати, в 270 вакансиях зачем-то хотят высшее образование :)

React — всё? Стоит ли переходить на Svelte и SolidJS 3
Разброс вакансий React по требуемому опыту 

На Svelte нашлось только 43 вакансии и ни одной без опыта.

React — всё? Стоит ли переходить на Svelte и SolidJS 4
Разброс вакансий Svelte по требуемому опыту 

По запросу «SolidJS» нашлось 508 вакансий. Но многие из них точно не целевые и подразумевают не этот фреймворк. Пример нецелевой вакансии:

React — всё? Стоит ли переходить на Svelte и SolidJS 5
Вакансии Solid

Скорее всего, под SolidJS реальных вакансий будет примерно столько же, как и под Svelte, если не меньше.

Ключевые преимущества для разных задач

Svelte лучше подходит для небольших проектов

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

SolidJS отличный выбор для высоконагруженных интерфейсов

Благодаря более точечной реактивности и компилируемости отлично подходит для проектов с высоко нагруженными интерфейсами.

Например, в дашбордах, графиках или редакторах (аналогичных Figma) SolidJS быстро реагирует на изменения данных, сохраняет плавность интерфейса даже под большой нагрузкой.

React — по-прежнему оптимальное решение, но не всегда лучшее

React все ещё подходит для большинства проектов, просто разработка на нем может быть дольше и сложнее из-за JSX. Также его не стоит использовать для проектов, которые предполагают работу в регионах с плохим интернет-соединением или слабым железом.

Мнения экспертов

Большинство старых проектов Бюро сделаны на экспрессе + разный JS на фронте. Такой стек сложно поддерживать и развивать. Поэтому мы начали делать новые проекты на Свелт. Задача была как можно плавнее перевести ребят с разными навыками на современный стек. В итоге проекты на Свелт запускаются гораздо проще и работают в разы быстрее. Это всех подкупает.

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

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

Смена фреймворка — слишком большой слом налаженных процессов. Это всегда очень больно и долго, и, как правило, того не стоит.
Миша Родштейнруководитель разработки в Дизайн-бюро «Интуиция»
И Svelte, и SolidJS обгоняют React по скорости и весят меньше. В частности, не последнюю роль в этом играет отсутствие Virtual DOM. Но для меня это важно только как часть кругозора. Если у вас в приложении наблюдаются проблемы с производительностью на React, с огромной долей вероятности в этом виноват плохой код, а не сама библиотека. Да, Реакт практически обязывает тащить за собой кучу дополнительных библиотек на все случаи жизни, которые также могут быть проблемой в производительности. Но в целом разница в скорости в пару десятков миллисекунд незаметна. Если у компании/команды есть экспертиза в React и его экосистеме — переходить на другие инструменты без сильной причины (просто для более красивых циферок) не вижу смысла.

Совсем другое дело, когда вы заранее знаете, что ваше приложение обещает быть тяжелым с точки зрения нагрузки или у конечных пользователей слабое железо (например, если вы выпускаете приложение на рынки Африки или Индии, для вас это актуально, тем более если им будут пользоваться с мобильных устройств). Но и здесь я бы не стал слепо отказываться от React’a — у него всё же есть много совместимых библиотек, легче найти решение проблемы. А вот с другими инструментами дела могут обстоять иначе, и не факт, что в лучшую сторону. Да, вы можете получить выигрыш в циферках производительности и веса сборки, но можно наступить на много граблей во время разработки, что сведет все преимущества на нет.

В целом я считаю важным периодически осваивать новые фреймворки и библиотеки, такие как Svelte и SolidJS. У них есть свои преимущества, и о них важно знать. Так, в моменты, когда стоит реализовать именно эти преимущества, вы сможете проявить себя, предложив новый инструмент. Более того, это позволяет поддерживать свой кругозор, как специалиста, и осваивать новые подходы. Рано или поздно эти знания могут пригодиться и в проектах на том же самом React или в любом другом решении. Просто знать, что можно и как разрабатывать — сильно повышает экспертизу разработчика!
Александр Гузенкоруководитель фронтенд разработки отдела RD в крупной европейской компании (к сожалению, под NDA)

Стоит ли уходить с React?

Зависит от проекта и ваших личных планов. Новичкам лучше оставаться на React — под него заточено большинство вакансий. Проект под SolidJS или Svelte будет найти сложнее, а на React уже написано много решений, которые надо поддерживать.

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

Тем, кто работает над новыми крупными проектами или поддерживает старые, лучше остаться на React. У него большое комьюнити и много библиотек. Да, он уступает другим библиотекам в производительности, но это отставание в большинстве проектов не будет критичным.

А какие фреймворками для разработки интерфейсов используете вы?
Следите за новыми постами
Следите за новыми постами по любимым темам
93 открытий300 показов