Как рендерится фронтенд. И как мы дошли до жизни такой

Frontend

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

Статическая генерация страниц

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

Под капотом это был какой-нибудь MVC-фреймворк, где сущности разделялись по зонам ответственности, но существовали рядом в довольно жестком зацеплении. Весь контент генерировался на сервере и отправлялся в браузер пользователя в виде готовых HTML-страниц. Быстро и надежно.

Но недостатков было больше:

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

И, конечно, мириться с этим было нельзя, поэтому произошел акт эволюции.

Single Page Applications (SPA)

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

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

По сути совершился переход от тонких клиентов к толстым.

О недостатках такого подхода мы все знаем уже на своем опыте:

  • Страница загружается долго, до первой интерактивности могут пройти секунды
  • Плохое SEO, потому что страница, которую парсят роботы пуста (весь контент создается в браузере пользователя)
  • Огромные куски JS, которые необходимы, чтобы пользователь увидел хоть что-то

Backend For Frontend (BFF)

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

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

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

Бэкенд остался неизменным. Однако клиент стал ходить туда через прослойку-адаптер — Backend For Frontend (BFF), которая среди прочего и взяла на себя функцию форматирования данных под нужный формат.

Таким образом каждый фронтендер, работающий с BFF, отчасти стал фулстеком. Проблема решена, можно возвращаться к устранению недостатков SPA.

SSG, ISR, SSR

Первое очевидное решение — отменить весь прогресс и вернуться к статической генерации, но, желательно, сохранив современные, уже привычные инструменты для строительства страниц. Так и появился SSG (Static Site Generation) — подход, при котором страницы генерируются на сервере во время сборки приложения, складываются в какое-то хранилище и потом раздаются через CDN. При таком подходе мы снова потеряли интерактивность, но вернули скорость загрузки.

Развитием этой идеи стала технология инкрементальной регенерации — ISR (Incremental Static Regeneration). При таком подходе статические страницы создаются на сервере после запроса пользователя и хранятся в неизменном виде. Когда заданное время жизни (TTL — time to live) истекает, страница пересоздается в фоновом режиме, не требуя полной сборки проекта. Это довольно экономично и быстро, но все равно ещё недостаточно интерактивно.

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

Уже лучше. Уже очень хорошо. Но есть еще один способ сделать отрисовку интерактивных страниц быстрее.

React Server Components (RSC)

5 декабря 2024 года состоялся релиз первой стабильной версии серверных компонентов React. Основная идея как будто вдохновлена ТРИЗ — нет компонента, нет проблемы. Суть подхода в том, чтобы не отправлять на клиент ничего, что не взаимодействует с браузером.

Проще говоря, рендеринг делится между сервером и клиентом.

Те сущности, которые ходят в БД, выглядят скорее статическими чем динамическими, не нуждаются во взаимодействии с браузерными API, исключаются из дерева компонентов и живут на сервере. Это серверные компоненты.

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

Что же мы получили в итоге:

  • быстрая гидрация, потому что компонентов, которые нужно «оживить» не так уж и много
  • маленькие бандлы JS, так как они содержат только клиентский код
  • повышенный уровень безопасности — чувствительный код может жить только на сервере

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

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

Что же использовать в итоге?

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

Симо Мофин
Симо Мофин

Senior Frontend Developer
Главный по блогу