Почему использовать react - плохое решение?

12 сентября 2021 г. 1:07

Вероятно, любой веб разработчик слышал о библиотеке react. Для тех, кто не в курсе react - это библиотека от фейсбук с открытым исходным кодом, которая благодаря реактивному подходу позволяет описывать веб-страницу в стиле близком к декларативному. Что означает "реактивный подход"?

Лучше один раз увидеть, чем сто раз услышать:

function Example() {
    const [count, setCount] = useState(0);
    return (
      <>
        <p>Вы кликнули {count} раз</p>
        <button onClick={() => setCount(count + 1)}>
          Кликни меня!
        </button>
      < />
    );
  }

Пример выше написан на jsx. Jsx - специальное расширение языка javascript, которое позволяет писать разметку непосредственно внутри javascript кода.

В этой разметке мы видим два элемента: p и button. В элементе p выводится переменная count (немного похоже на то, как это делается в шаблонных литералах). У button же иная роль: при нажатии триггерить событие, которое меняет значение этой переменной. И при каждом изменении переменной реакт автоматически меняет значения всех узлов (node), которые ее содержат. Это называется реактивный подход. И по сути такой подход является простейшим случаем реализации паттерна MVVM.

Казалось бы, ничего сложного описать подобный функционал и на чистом js. И на самом деле это, может быть, было бы даже проще: достаточно найти элемент, который выводит переменную count и обновить ему innerText:

<button onclick='document.querySelector('.some_selector').innerText += count + 1'>
          Кликни меня!
</button>

Но давайте представим, что переменная count может использоваться не в одном элементе, а в n-ом количестве, и мы даже не можем это количество предугадать, пока разработка не дойдет до финальной стадии. А после финальной стадии еще потребуется и поддержка. В таком случае нам придется постоянно обновлять обработчик onclick, чтобы поддерживать отображение в актуальном состоянии. Реактивный подход позволяет решать эту проблему. Сегодня такой подход считается хорошим тоном (и в подавляющем большинстве случаев так и есть)

Кроме того, react позволяет использовать в приложениях на его основе компонентный подход и опционально изоляцию стилей, что так же является хорошим тоном

Почему же не стоит использовать react в реальных приложениях?

И вроде бы все замечательно. Но как говорится, бог в мелочах а дьявол в деталях.

Несмотря на все усилия мэйнтэйнеров react сделать его максимально удобным для разработчиков, постоянное улучшение производительности и потребления памяти, на данный момент он является чуть ли не худшим технических решением для фронтэнда. Что??? Нет! Я не опечатался. И это не кликбэйт! Я не отговариваю вас писать на react! Заголовок этой заметки не претендует на то, чтобы отказаться от реакта во всех возможных кейсах. Ведь есть решения, где являются решающими исключительно удобство и скорость разработки. А в этом плане реакт может побороться за пальму первенства, хотя на сегодняшний день, вероятно, и ее придется разделить. И если вам не понятно, что за бред я только что написал, добро пожаловать под кат.

Обо все по порядку

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

  • Отзывчивость (скорость рендеринга элементов)
  • Потребление памяти
  • Размер бандла

А если не задуматься еще и о SEO, то за пользователей (точнее за их отсутствие) заплатит еще и заказчик (не в хорошем смысле).

Теперь давайте остановимся на каждом пункте поподробнее:

Отзывчивость (скорость рендеринга)

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

На скрине показана производительность перерендеринга элементов при добавлении и удалении записей из массива значений, который они отображают, с использованием различных инструментов, использующих такой же "реактивный подход", как и react. И как видим, на сегодняшний react показывает далеко не лучшие результаты. Из тестируемых решений ему уступают всего лишь knockout и blazor. Но к ним мы вернемся чуть позже.

Не сильно отличается и картина при первоначальном рендеринге (инициализации компонентов):

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

Потребление памяти

Обратимся к тому же самому репозиторию от krausest. На скрине ниже мы видим потребление памяти разных фреймворков/библиотек при различных операциях:

Можно было бы подумать, что react уступает по производительности конкурентам, поскольку экономит память клиента. Но оказывается, что и это не так. По потреблению памяти react так же занимает почетное место с конца (не в хорошем смысле), уступив всего лишь preact и knockout.

Размер бандла

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

Для определения размеров бандла пакетов, как правило, используют сервис bundlephobia. Но тут есть нюансы, о которых нужно знать: зачастую от ньюфагов react можно услышать фразу "Да что он весит? Каких-то 7 kb?". И да, и нет. Дело в том, что react не имеет никакого смысла без подключения пакета react-dom, который является связующим звеном между react и DOM. И именно в ReactDOM находится функция render, которая отвечает за рендеринг компонента react в DOM-дерево. Таким образом, чтобы использовать react-приложение в веб, вам понадобится 7kb + 121kb = 128kb. И это только "батарейки", т.е. это размер бандла для приложения типа "hello world", и это не предел, поскольку в эти 128kb не входят ни state manager (пусть и можно из коробки реализовать его подобие на useContext & useReducer, но такое решение может быть относительно удобно только для простых приложений), ни хваленые styled components, ни роутеры. Все это нужно подключать отдельно, так же, как и платить за эти подключения новыми килобайтами.

Если идти по этому пути прямо и не сворачивать, то в один прекрасный момент можно стать автором подобного поста. Если вас устраивает минфицированный бандл размером 200-300kb, то можете пропустить этот пункт. Но для тех, кто выбирает лучшее, предлагаю посмотреть на альтернативы:

  • preact (preactjs/preact) - библиотека наподобие React. В экосистеме есть роутеры, аналоги styled components в виде linaria и goober, поддержка css модулей via postcss, поддерживается стэйт-менеджарами, умеющими в ts, в частности nanostores и прочими. Весит всего 10 kb. Однако есть небольшой нюанс: в эти 10kb не входят preact/hooks. В совокупности - 10kb + 10kb = 20kb. Хотя в этом бойлерплэйте у меня получилось даже меньше, 12kb

  • lit-element (lit/element) - реактивная библиотека на основе веб-компонентов. Изоляция стилей идет из коробки. Tree-shakeble. В экосистеме можно подключить пакет spa routers. 25kb.

  • inferno (infernojs/inferno) - react-like библиотека. Возможна изоляция стилей через postcss модули. Есть стэйт-менеджер, например, этот (не пробовал). 21kb.

  • svelte (sveltejs/svelte) - так называемый "исчезающий" фреймворк, 100% tree-shakeble. Его еще называют "компилятором", поскольку он оставляет в рантайме исключительно используемые сущности. В экосистеме присутствует богатый выбор роутеров на любой вкус. Поддерживает typescript, из коробки идет state manager. Размер "hello world"-а - 4kb

  • vue (vuejs/vue) - реактивный фреймворк от бывшего сотрудника гугл. Имеет очень богатую экосистему. Из коробки идет изоляция стилей. Vuex (12kb) и vue routers (28 kb) подключаются отдельно. Размер "hello world"-а - 64kb

  • knockout (knockout/knockout) - реактивная библиотека, работающая в нативном DOM (без виртуального DOM). Для изоляции стилей и компонентного подхода можно использовать веб-компоненты (они ничего не весят). Размер самого пакета 66kb.

  • blazor (dotnet/blazor) - реактивный шаблонизатор из экосистемы .NET Core (поддерживает .NET языки). Реактивность осуществляется за счет взаимодействия с сервером через вебсокеты (вся работа по хранению стэйта ведется на сервере) по принципу django-reactor и elixir live phoenix. Это, пожалуй, самый тяжелый бандл из всех перечисленных, 215kb. И тут, казалось бы на первый взгляд, React был бы куда более предпочтительным. Но давайте заглянем под капот: в отличие от react-овского в него включен spa-роутер, клиент-серверное взаимодействие через websocket-ы (в React, чтобы реализовать аналог пришлось бы писать такое взаимодействие с нуля на сыром Websocket + лонгпулл на случай, когда сокеты недоступны (это тоже предусмотрено в Blazor) либо использовать socket.io-client) и runtime проверка типов (это что-то наподобие ts-runtime или runtypes + typescript из мира js, таким из коробки не может похвастать даже ангуляр). И давайте посчитаем, сколько бы это заняло в мире React: базовый бандл (128kb), react-router (21 kb), runtypes (29.1kb), socket.io-client (48kb). Итого 226kb. Вероятно, даже Blazor неплохо выглядит на фоне реакта

Поехали дальше:

SEO

После того, как фейсбук анонсировали новую библиотеку, она сразу начала набирать популярность. И хотя она была не первой, именно она, благодаря более низкому порогу входа, чем у angular и ember, стала спусковым крючком для тренда на реактивные фреймворки. И все бы ничего. Но со временем оказалось, что React со своим клиентским рендерингом не отвечает главному требованию бизнеса для b2c систем. Во-первых, выяснилось, что поисковики намного хуже индексируют страницы с динамическим контентом: в случае серверных шаблонизаторов (blade, django-templates, jinja2 и даже простых php-скриптов) поисковики сразу получали статический контент, парсили его и отдавали в выдачу. С динамическим контентом все сложнее: чтобы его получить, необходимо выполнять js-код. И в принципе, современные поисковики это уже умеют, но тем не менее такие страницы индексируются на порядок хуже. Во-вторых - это уже касалось слабых компов - отзывчивость приложения, использующего клиентский рендеринг, как оказалось, напрямую зависит от мощности компьютера/смартфона клиента, и на слабых машинах заметно притормаживает. Для решения этих двух проблем был придуман (точнее сказать, переизобретен) ssr (server side rendering): да, для реакта был создан Next.

По сути Next - это инструмент для рендеринга react-страницы на стороне сервера. Если вы хотите знать мое мнение, то это очень удобный инструмент, но для того, чтобы он выполнял свои функции, необходимо установить и настроить его на сервере. Соответственно это требует на сервере отдельного node-демона. В принципе, это неплохо вписывается для сервера на express либо для любого другого бэка на ноде. Но если у вас бэк на python или go, то это требует определенных затрат, поскольку мы получаем, что для того, чтобы запустить обычное веб-приложение, которое будет хорошо индексироваться поисковиками, нам необходим фоновый процесс, при чем в n раз более жирный, чем весь наш бэк (да, node любит покушать память).

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

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

knockout

Например, knockout вполне себе позволяет ввести в html дефолтное значение, которое будет доступно до рендеринга:

< p>First name: <input data-bind="value: firstName" / ></p>
< p>Last name: <input data-bind="value: lastName" / ></p>
< h2 >Hello, <span data-bind="text: fullName"> < / span>Planet Earth  by default!</ h2>
< script>
var ViewModel = function(first, last) {
    this.firstName = ko.observable(first);
    this.lastName = ko.observable(last);
    this.fullName = ko.pureComputed(function() {
        return this.firstName() + " " + this.lastName();
    }, this);
}; 
ko.applyBindings(new ViewModel("Planet", "Earth"));
</script>

В этом примере текст Hello, Planet Earth by default! будет доступен на этапе загрузки страницы без всяких тяжелых решений наподобие server side rendering.

Blazor

Поскольку Blazor формирует контент страницы полностью на бэке, то в момент загрузки, она будет полностью сформирована (проверено).

Web-componеnts

Веб-компоненты - это часть стандарта, на котором основан lit-element. Сами веб-компоненты не поддерживают реактивность из коробки, однако они поддерживают нативно изоляцию стилей и компонентный подход. Для реактивности же есть lit. По поводу seo с веб-компонентами есть хорошая статья. Если кратко, то все сео значимые элементы размещаются в качестве слота веб-компонента. И их содежимое прекрасно индексируется поисковиками

Почему же компании выбирают react?

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

Разработчики, которые делают выбор в пользу react, как правило, перечисляют следующие причины:

  • Мэйнтейнером библиотеки является корпорация фейсбук. Facebook сам использует react, а значит у этой библиотеки большое будущее

  • Для react есть много библиотек, совместимых с них. Куча готовых батареек, что означает минимум кода - максимум результата

  • Для react есть react-native, который позволяет писать приложение под web и mobile с одинаковой кодовой базой, что существенно упрощает поддержку и разработку приложения. Не надо держать двух разработчиков, если можно обойтись одним

  • У React большое комьюнити, которое позволяет быстро найти ответ на любой вопрос (как правило, это аргумент новичков)

  • Еще иногда приводят довод о том, что у реакт отличная поддержка typescript по сравнению с другими решениями (вероятно, ангуляр разработчикам это будет трудно понять)

Давайте разберем каждый пункт подробно

Поддержка библиотеки от facebook

Facebook прилагает много усилий к развитию react. Вероятно, компания и дальше будет продолжать вкладывать в развитие этого продукта. Действительно, вероятность, что его бросят, не высока. Но тем не менее, по моему скромному мнению, "качество" этого продукта оставляет желать лучшего: оно потребляет много памяти, много весит и работает медленно относительно других решений. Стоит ли доверять качество своего продукта ментэйнеру, который далек от принципов перфекционизма (видимо, в силу своего положения на рынке), решать вам

Батарейки

Для react действительно огромное количество готовых решений. Именно благодаря этому, разработка на реакте иной раз напоминает конструктор. Это удобно. Но действительно ли этот аргумент может стать решающим в принятии решения о выборе базовой библиотеки для своего продукта? Ведь если продукт уникальный, то его реализация требует гибкости, а не типовых решений. И если выбор делает настоящий разработчик, а не формошлеп, то для него не составит труда в реализации той или иной фичи, панели или слайдера на основе ванильного решения. Более того, эта реализация не заберет кучу времени и будет лаконично вписываться в архитектуру выбранного фреймворка. Было время, когда мне потребовался state manager для проекта на preact (preact не поддерживает большинство популярных state менеджеров реакта). Я не считаю себя гением, но я реализовал его за час (больше времени заняло оформление репозитория). Затем через несколько дней вернулся к нему и добавил поддержку typescript (еще 1 час). Я и сейчас считаю, что в разы больше времени я бы мог потратить на попытки адаптировать различные готовые решения с github. Но это уже на ваше усмотрение

React-native

React native - действительно серьезный аргумент в пользу React. Однако, не всегда все так просто, как во фразе "пишешь один раз - используешь везде". На самом деле, создание таких монорепозитриев, нацеленных на web и mobile одновременно, довольно нетривиальная задача. Она требует довольно высокой квалификации разработчика, который должен уметь разбираться как в деталях web, так и в особенностях android и ios разработки, уметь писать нативные модули на kotlin (java) и swift, знать особенности обеих платформ. Это довольно тернистый путь.

Во многих случаях проще взять какую-нибудь платформу, изначально предназначенную под мобильную разработку и писать все на одном языке, например, flutter, который, кстати, c недавних пор поддерживает транспиляцию и под web и поддерживается непосредственно гуглом, а значит может составить вполне достойную конкуренцию дуэту react+react native. Кроме того, по этому пути сейчас пошел и MAUI, новый кроссплатформенный фреймворк от компании Microsoft, которая добавила в него поддержку Blazor. А JetBrains приготовило нам подарок в виде JetPack compose, который уже нацелили на web, и я не помню ни одного случая, когда JetBrains не доводило задуманного до конца. Так что на этом поприще у react native намечается множество достойных конкурентов

Но даже если выбор пал на react native, что мешает использовать alias-ы для preact над react в конфиге своего сборщика? Отвечая сам на свой вопрос предположу, что, вероятнее всего, причиной остается хайп, который сформировался над реактом и затмил собой все остальное. А хайп чего-либо - суть реклама, главная функция которой, как известно - это информированность общества. Так вот причина, вероятно, в неправильной информированности сообщества об эффективности тех или иных инструментов. И автор надеется, что настоящая статья должна хоть немного, но помочь исправить эту ситуацию.

Комьюнити

Это несомненно аргумент, но насколько он серьезный? Как правило, он беспокоит начинающих, на которых уж точно не стоит делать ставку бизнесу. В современном мире наличие хорошей документации, очень редко вызывает много вопросов у опытных разработчиков. Так вот у preact хорошая документация, у svelte отличная документация (и так же большое комьюнити), у knockout хороший сайт, по lit полно примеров в интернете... А blazor - и вовсе родная среда любого .NET разработчика с огромными комьюнити за спиной. Ну и в конце концов, опытный разработчик и вовсе вряд ли будет нуждаться в комьюнити, поскольку все, что ему нужно, будет лежать перед ним

Поддержка typescript

Практически все представленные выше библиотеки в той или иной степени поддерживают typescript, поэтому трудно этот аргумент считать серьезным. Например, любопытно, чем preact в поддержке типов уступает react-у?

Для кого подходит реакт?

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

  • b2b системы - для b2b систем, как правило, не важно, сколько весит бандл и насколько быстро он работает. Как правило, пользователи b2b систем сидят с мощных машин с хорошим интернетом. Там работает другое правило: если б2б система тормозит и вы не можете найти денег на новую, значит вам тут не место, а значит бизнес найдет денег, чтобы поддерживать свой технопарк в тонусе. Если же не найдет, то о таких пользователях можно не заботиться, поскольку они таким системам не нужны. Кроме того, б2б не нуждается в ssr и сео-оптимизациях, а значит в дополнительных костылях и накладных расходах
  • премиум сегмент - это топовые смартфоны, которые потянут любой клиентский рендеринг. Бизнес, рассчитанный на премиум сегмент, если оно нужно, уж точно найдет средства на дополнительные мощности под ssr. Если же нет, то ему там не место. Единственным минусом мог бы быть размер бандла, который бы мог бы повлиять на скорость загрузки при плохом интернете. Но я нахожу этому одно оправдание: клиент с премиум машиной должен страдать
  • монополисты - это категория компаний, которым глубоко безразличен user-expierence своих пользователей, потому что у них и так все хорошо. И какое бы ужасное решение они не выкатили в продакшн, они всегда будут знать, что им будут пользоваться. Страдать, но пользоваться. И они ни разу не потеряют ни одного клиента из-за своего плохого ui/ux. Наглядные примеры - это сайт втб, www.gosuslugi.ru, сайт газпрома, билайн, aliexpress, лента яндекс.дзен и прочие

Так же в некоторых случаях к этим трем типам можно добавить сегмент с узкоспециализированными инструментами, где скорость работы и объем скриптов реакта просто теряются на общем фоне. Хотя. С этим тоже можно поспорить при желании. Ярким примером такого инструмента является Figma.

Вместо заключения

Итак, использовать ли вам реакт, решать исключительно вам. Я постарался привести все плюсы и минусы разработки на реакте, настолько объективно, насколько смог. Если говорить о моем мнении, я бы присмотрелся к альтернативным решениям. Помимо вышеперечисленных, ради общего развития так же посоветовал бы обратить внимание на mitosis. На этом я все. Пишите свои мысли в комментариях. Всем успехов и до новых встреч!

admin
(ваш голос учтен)