amorgunov
1.96K subscribers
16 photos
1 video
101 links
Пишу о JavaScript и его экосистеме (TypeScript, React, NextJS), о процессе разработки и архитектуре.

Блог: https://amorgunov.com
По всем вопросам (рекламу не продаю): @saaaaaaaaasha
Download Telegram
Сегодня ночью (уже через пару часов на самом деле) состоится конференция по NextJS (https://nextjs.org/conf). Я точно буду смотреть (возможно в записи), после напишу про самые интересные поинты.

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

⚠️ Роутинг на основе файлов, что (1) неудобно для работы с SPA/SSR, так как файл = роут, (2) делает компоненты страниц супер ответственными за все подряд (за загрузку данных, за обрабатываемый путь, за доступность страницы и т.д.) и (3) накладывает ограничения стиля кода (типа, обязательно использовать export default или именование файлов);

⚠️ Сложный жизненный цикл методов, которые могут выполняться и на сервере, и при определенных обстоятельствах на клиенте или специальном режиме запуска. Сюда же сложность с синхронизацией серверного и клиентского стейтов (так как переход на другую страницу запускает создание нового стейта, который создается с нуля);

⚠️ «Бедная» экосистема. Бедная не в плане того, что мало библиотек, нет. Бедная в том, что нам не удалось подключить ни одну библиотеку без проблем, везде возникали странности при использовании, которых казалось бы не должно быть (примеры в посте);

⚠️ Установка неявных зависимостей;

⚠️ Самая главная проблема на мой взгляд в том, что NextJS пытается решить сразу все и быть универсальным решением для всего.  NextJS работает и с генерацией статических сайтов (SSG), и с серверным рендерингом (SSR), и просто как с обычным приложением на React (CRA). Для каждого подхода есть свои особенности и практики, которые нереально уместить в одном фрейморке, не сделав его монструозным для использования.

Я думаю, что через 3-4 месяца, когда мы доведем проект до продакшена, я напишу вторую часть, так как первое впечатление может быть обманчиво. Она будет либо о том, что несмотря ни на что, это было верное решение - использовать некст (надеюсь на это), либо - список с проблемами пополнится.
Что рассказали на конференции NextJS Conf (Turbopack)

Пару дней назад прошла конференция NextJS Conf по новой версии NextJS 13. Много о чем рассказывали, но я выделил три вещи:

1️⃣ Новый бандлер Turbopack на Rust (спикеры говорили про него, как Webpack successor → приемник Webpack)

2️⃣ Интеграция новых фич из React18

3️⃣ Небольшие улучшения встроенных компонентов (next/image, next/font и т.д.)

Сейчас про 1️⃣ Turbopack. Пока это NextJS-first решение, которое нацелено на работу с NextJS приложениями, но в дальнейшем его хотят сделать фреймворко-независимым. Под капотом используется SWC компилятор для сборки JavaScript/TypeScript и своя система кеширования. Сейчас кеш хранится в памяти, в будущем его хотят сохранять на жесткий диск или удаленно, чтобы в рамках одной команды, разработчики (или раннеры в CI) могли использовать кеш для более быстрой сборки. Судя по документации, кеширование устроено за счет мемоизации функций на уровне компилятора.

Рассказали, почему решили писать свое решение. Сейчас используется Webpack, из бандлеров нового поколения рассматривали Vite и Esbuild. Vite медленный для холодного старта (первого открытия приложения) на больших проектах (из-за большого количества сетевых запросов), использует для продакшена Rollup (медленный) и разные среды выполнения для dev и prod окружений. Esbuild не умеет в HMR (горячую перезагрузку) и имеет ограниченную систему плагинов.

По замерам (которые представили на конференции) Turbopack в сотни раз быстрее вебпака, и в 10 раз быстрее Vite. На самом деле здесь немного приукрасили, горячая перезагрузка даже на огромных проектах плюс-минус одинакова. Речь только про холодный старт.

Автор Vite, Эван Ю, тоже прокомментировал в твиттере выход нового сборщика, в том числе прошелся по озвученным минусам Vite. Проблема с холодным стартом есть, но прямо сейчас идут размышления, как это решить (в пример он привел будущую фичу браузеров - web bundles, возможность упаковать все файлы проекта в бандл и отправить их в браузер одним запросом). Честно, я о web bundles раньше даже не слышал, интересная фича. Про сборку продакшен версии, Эван сказал, что в любой момент можно переключиться на SWC (сейчас этого не делают, чтобы не тянуть зависимость) или тот-же Turbopack (сам тред).

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

Про интеграцию новых фич из React18 расскажу отдельно, но если кратко, то на основе Transition API, Suspense, серверных компонентов и нового потокового стриминга, в Next13 реализовали новый подход к работе с лайаутами (включая Nested Layouts) и загрузкой данных.
Серверные компоненты в React

Начал я писать про еще одно нововведение в NextJS 13 - лайауты, как понял, что сначала стоит рассказать про React Server Components (RSC).

Уже два года назад был выложен RFС (Ready for comment, документ с описанием фичи) по серверным компонентам (reactjs/rfcs/0188-server-components.md), но какую-либо популярность такие компоненты пока не обрели. Я думаю вы либо не слышали про них, либо не использовали (а если использовали, поделитесь в комментариях впечатлениями).

Если в двух словах, это такие компоненты, которые рендерятся только на сервере и код которых не попадает в клиентский бандл (в отличие от обычного SSR). Это позволяет на клиент загружать JS-код только интерактивных компонентов (zero-bundle-size components), реализовать более простой код сплиттинг, избавиться от client-server waterfalls (когда мы сразу в нескольких компонентах загружаем данные внутри useEffect и одновременно создаем N запросов на сервер). По сути это попытка реализовать частичную регидрацию (partial rehydration), когда мы "гидрируем" только клиентские компоненты.

Все бы ничего (и даже в теории очень круто), но помимо технических проблем (типа клиентского роутинга и кеширование компонентов на сервере), у RSC очень ужасный DX (developer experience). (1) Свой API использования, (2) сложный жизненный цикл, (3) необходимость настраивать сервер и (4) отсутствие документации. Раньше, при обычном SSR, у нас были изоморфные компоненты, которые могли рендериться и на сервере, и на клиенте. Да, на сервере не работали хуки useEffect, для каких-то компонентов нужно было явно возвращать null при рендеринге на сервере, но они были универсальными. Сейчас же (в контексте RSC) универсальные компоненты из себя представляют чистые функции (в которых нельзя использовать хуки), и они просто возвращают JSX. Серверные компоненты представляют собой вообще отдельный вид функций (с async-await, без хуков).

Чтобы отличить клиентские и серверные компоненты, придумали использовать директиву "use client" в начале файла, что тоже на мой взгляд является проблемой. Помимо разработчика и React-а, эту директиру должен понимать сборщик (и если Webpack это умеет, то, например, для Vite, нет реализации), IDE, eslint и другие инструменты. И кстати, внешние библиотеки тоже (reactjs/rfcs/pull/227#issuecomment-1301626536). Явное разделение компонентов на серверные и клиентские без правильной архитектуру сделает код в разы сложнее, чем это можно было сделать с самописным SSR.

Возможно, если для RSC сделать API как у клиентских, избавиться от явного указания директивы (чтобы бандлеры сами определяли, серверный компонент или нет), то фича сдвинется с места. Но пока по моим ощущениям реализовывать вручную в своих проектах серверные компоненты никто не будет, и использоваться они будут только под капотом больших фреймворков, типа NextJS (что и произошло в последнем релизе).
WebApp patterns

Попался очень крутой сборник паттернов для разработки веб приложений: https://www.patterns.dev/ от Lydia Hallie и Addy Osmani.

Все поделено на 3 основные топика: паттерны архитектуры, рендеринга и оптимизации. Даже про React Server Components и различные виды гидрации есть информация. Не слышали про какое-то определение с сайта? Самое время с ним познакомится, так как все приведенные паттерны сейчас актуальны.

Как бонус, по ссылке https://www.patterns.dev/posts/classic-design-patterns/ доступна книга Эдди Османи «Learning JavaScript Design Patterns». Хоть в ней много устаревших технологий (типа jQuery или AMD модулей), это не мешает ей рассказывать о паттернах проектирование в JavaScript.

---

Пару слов об авторах. Если с работами Лидии я ранее не был знаком, то с Эдди я знаком еще с 2014-2015 года, с его работами конечно же. Мой первый проект на JS был на библиотеке Backbone/Marionette (кто помнит эти фреймворки?) и помочь в нем разобраться помогла книга
«Developing Backbone.js Applications» от O'Really за авторством Эдди. Эта одна из немногих книг по разработке, которую я полностью прочитал (а это многого стоит с моей то усидчивостью). Из последнего (кроме patterns.dev) он написал огромное руководство про оптимизацию изображений (которое тоже позже оформили в книгу). По итогу, если встречаете какой-то материал от Эдди, то от меня автоматически рекомендация на его изучение :).
И кстати, сейчас проходит одна из самых больших конференций по JS в ру-сегменте - HolyJS. Завтра (11/11) community-день, который будет бесплатным (чтобы получить доступ, нужно просто зарегистрироваться).

Я бы не сказал, что будут какие-то громкие доклады, но есть интересные темы, про PWA, опыт использования эффектора (стейт-менеджер, который мне не зашел), свой рендерер (react-reconciler) на примере игры, очередной доклад про то, как улучшать перформанс приложения. Полное расписание (и там же есть кнопка для регистрации) доступно по ссылке: https://holyjs.ru/schedule/days/2/ .

А на youtube канале HolyJS выложили доклады с весенней конференции, держите ссылку на плейлист. Я еще не добрался посмотреть, но по списку, там много крутых спикеров выступало, должно быть интересно.
Привет, друзья. Вижу по реакциям, что анонс бесплатного дня HolyJS не зашел. А вообще, хотели бы на канале видеть анонсы различных конференций или оставить это новостным каналам?

Например, недели две назад прошла конференция perfnow() 2022 (https://perfnow.nl/speakers), посвященная веб перфомансу и метрикам. Я конечно же ее пропустил, но по описанию были крутые доклады. В паблике пока доступны записи двух из них (ссылка на плейлист).

1️⃣ «What’s new in performance?» - обзор новых фичей для оптимизации веба.
Back/forward cache для быстрого перехода через браузерные кнопки вперед/назад, про приоритет загрузки ресурсов Priority Hints, новый (относительно новый) HTTP статус 103 Early Hints, новую метрику Interaction to Next Paint, браузерное API isInputPending(), который возвращает true, если пользователь взаимодействует со страницей (например, вводит символ в инпут), и функцию postTask, которая позволяет планировать вызовы функций (задач) с нужным приоритетом. И еще про новый способ замера метрик производительности не просто для загрузки страницы, а для прохождения пользовательского сценария через puppeteer и Lighthouse.

2️⃣ «Early Learnings for Early Hints at Shopify» про оптимизацию с помощью Early Hints (http статус 103, который позволит браузеру предзагружать ресурсы до отдачи index.html). Например, пока на сервере подготавливается страница, браузер может предзагрузить шрифты или установить DNS-соединение с CDN. Все это можно сделать и сейчас с помощью атрибутов preconnect и preload, но уже после получения html-страницы (в отличие от Early Hints). По замерам из доклада, Early Hints + preconnect показывает хорошие результаты и уже сейчас улучшает показатель метрики Largest Contentful Paint (LCP), хоть и незначительно (на 1-2%). Основная проблема - сложность на стороне сервера научиться возвращать нужный статус и заголовки, указывающие, какие ресурсы нужно предзагружать. Ну и пока эту фичу поддерживает только Chrome.

Кстати, там же выступал Иван Акулов (@iamakulov_channel) с материалом про новый параллельный режим работы React-а (пока к сожалению без записи).

В общем, давайте проголосуем:

👍 - Скидывать все анонсы конференций, которые посчитаю нужным
👎 - Не скидывать
👌 - Скидывать точечные анонсы профильных конференций и обзоры интересных докладов
Что нового в NextJs 13 - Layouts

Все же хочу завершить серию постов про NextJS, ждите еще пару в ближайшее время. В этот раз про новую (13) версию NextJS. И если в прошлый раз я писал про Turbopack, то сегодня про другое нововведение. Turbopack, кстати, продолжают активно обсуждать в твиттере, автор Vite/Vue (Эван Ю) запускает бенчмарки у себя и находит странные детали, что не такие уж бенчмарки и честные.

Неделю назад я рассказывал про RSC - серверные компоненты. Используя их, а также, Transition и Suspense API, стриминговый рендеринг (фичи из последних версий реакта) реализовали новую версию Layouts (пока только в beta). Простыми словами - систему навигации по приложению.

Придумали новый механизм работы со страницами, лайаутами (включая вложенные) и загрузкой данных. На конференции был 23-минутный доклад, где продемонстрировали новый подход (посмотреть можно на youtube). Теперь все страницы по умолчанию - это RSC. По сути это асинхронные функции, которые рендерятся только на стороне сервера и в которых можно делать запросы за данными через async-await (правда в NextJS пока это реализовано через экспериментальный хук use).

С одной стороны, круто, что команда NextJS пытается внедрить фичи из React, а не реализовывать их с нуля. Теперь в NextJS можно использовать RSC (*), потоковый ренденринг, роутинг с частичной гидрацией (сам лайаут страницы статичен, и обновляется только контент внутри) и собирать все это в продакшен-бандлы из коробки. Ден Абрамов в твиттере написал тред после конференции и сказал, что 13 версия - это наиболее полная попытка реализовать новую архитектуру React, соединяя все части вместе.

Однако есть обратная сторона. Пока это довольно сырая фича, которую нельзя использовать в продакшене (не просто так она в бете). Под капотом используются RSC, появившиеся недавно и с которыми мало кто умеет работать. Вручную RSC настроить намного сложнее, чем обычный SSR, поэтому они сейчас почти нигде не используется кроме нескольких подобных фреймворков. Про DX серверных компонентов я уже писал, что он плох, а их придется использовать прямо по текущей спеке.

Моя личная боль - для работы с новым подходом опять использовали файловый роутинг (только теперь все это лежит не в директории pages, а в директории app). В каждом приложении должна появиться такая директория, в которой будут описаны страницы, например:

app/
movies/
[id]/
page.ts
layout.ts
page.ts
error.ts // ошибка
layout.ts // лайаут
loading.ts // лоадер
page.ts // сама страница


Для небольших проектов или проектов с SSG (Static Site Generation, которые полностью генерируются на этапе сборки) выглядит удобно, но для больших проектов с десятками страниц нужно намного больше гибкости.

Серверные компоненты могут жить только в этой директории, тем самым приложение разделяется на две части - клиентскую и серверную (которую не можем переиспользовать на клиенте). Например, есть модуль "Auth", все его составляющие лежат в features/Auth, а его серверные компоненты должны жить в app, что выглядит как минимум странно. Я всегда борюсь с тем, чтобы компоненты в приложении были разбиты не по страницам, а по доменным сущностям, но здесь мы откатываемся назад.

Если в RSC нужно использовать какую-то внешнюю библиотеку, у которой явно не прописана директива "use client" в начале файла, то при подключении возникнет ошибка и нужно создать свой "клиентский" компонент враппер. Если нужно использовать какой-нибудь хук (useEffect, useContext), то же самое, создавайте враппер - хуки не работают в RSC. И это только проблемы, которые лежат на поверхности.

У команды из NextJS очень сложная задача по дальнейшему развитию этой фичи. Нужно активно собирать фидбек, думать над улучшением DX, так как в текущем виде реализация очень спорная. Посмотрим, что будет дальше.

Бандлер Turbopack в альфе, Layouts в бете, есть ли смысл использовать новую версию NextJS? На самом деле есть, и на рабочем проекте мы это сделаем из-за еще одной фичи. Но о ней в следующий раз.
Серверная часть NextJS

Любой фреймворк, поддерживающий SSR из коробки, можно считать «фуллстек» фреймворком. У таких решений помимо клиентской части есть и серверная. NextJS не исключение, у него под капотом есть встроенный сервер (на основе NodeJS-модуля http), запускающийся при вызове команды next dev или next start).

Если мы работаем над энтерпрайз приложением, то, вероятно, встроенный в NextJS сервер уже не подойдет из-за ограниченного функционала. Поэтому обычно запускают свой (express, fastify), внутри которого программно создают экземпляр NextJS-приложения (https://nextjs.org/docs/advanced-features/custom-server). В рамках этого сервера обрабатывают все необходимые инфраструктурные и не только задачи (кеширование ответов, работа с метриками для мониторинга, настройка ручек «живости» контейнера, предзагрузка данных).

Тут хочется сказать пару слов про подход BFF (Backend for frontend), под которым обычно подразумевают легковесный веб-сервер. На нем можно проксировать запросы в бекенд(ы), сетить куки и http-заголовки, маппить данные с разных источников, обрабатывать редиректы, обрабатывать SSR и т.д. Это своего рода API Gateway, используемый под конкретный тип клиентского приложения (например, мобильного или веб-приложения). Кастомный сервер для NextJS, это тот же BFF.

Еще из особенностей, за BFF обычно ответственны фронтенд разработчики (правда не всегда).

В NextJS 12 появился встроенный механизм миддлвар (сеттинг кук и заголовков, редиректы), позволяющий не настраивать свой сервер. Но в таких миддлварах нельзя было вернуть свой ответ, т.е. нельзя было отдать готовую html-страницу (или данные) из кеша и отправить ее пользователю, из-за чего все равно приходилось поднимать свой сервер.

А уже в 13 версии появился функционал, позволяющий возвращать кастомный ответ из миддлевар. И мы можем отказаться от программного запуска NextJS, так как теперь есть все для полной обработки запроса.

Собственно из-за этой фичи у меня есть желание мигрировать проект с 12 версии на 13 (благо это будет не сложно, так как мы еще не в продакшене и нестрашно будет что-то сломать в процессе) и попробовать реализовать BFF чисто на встроенном механизме миддлвар. Единственная проблема, что для готовых веб-серверов (express) уже написано много библиотек, которые не получится переиспользовать.
Трофей тестирования

В прошлом году я выступал на нескольких митапах, где рассказывал про тестирование фронтенда. Одна из самых интересных вещей на мой взгляд - это трофей тестирования и слои, из которых он состоит.

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

В общем это модифицированная пирамида тестирования, но только для фронтенда. Аккумулировал знания по трофею в виде отдельного поста в блоге: https://amorgunov.com/posts/2023-04-01-testing-trophy/

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

Мое первое впечатление о NextJS сложилось еще полгода назад, когда я написал пост: «Почему я не использую NextJS» (читайте на блоге: https://amorgunov.com/posts/2022-10-14-why-i-am-not-using-nextjs/). Название кликбейтное, но на самом деле хотелось поделиться проблемами, которые, казалось бы, не должно возникать у фреймворка такого масштаба.

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

Прошло полгода и я могу сформулировать еще одну крупную проблему. NextJS в режиме SSR (или ISR, Incremental Static Regeneration, где тоже нужен сервер) не приспособлен к деплою на self-hosted машины. А если быть точнее, ничего не доступно из коробки. Метрики отправлять можно только через кастомный сервер (подключив поверх express или fastify), который не рекомендуют использовать. Нет гибких методов кеширования, кроме http-кеширования на уровне страницы. Логгер можно подключить только через monkey-patching. Никаких оптимизаций для отказоустойчивости и боль при работе с картинками (расскажу отдельно). Кстати, что касается метрик, то нет публичного API, чтобы мерить время серверного рендеринга JSX → HTML.

Если приложение деплоить на серверах Vercel (компания, которая разрабатывает NextJS), то там многие проблемы решены (так как настроена инфраструктура). Еще есть готовые решения мониторинга и аналитики сервера, типа https://newrelic.com/, которые так же закрывают часть проблем. Однако, (1) это дорого, (2) в текущих реалиях нельзя использовать и (3) тот же NewRelic, например, для сбора метрик тупо патчит внутренние методы NextJS (тут можете подробнее по изучать).

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

Я не первый раз ругаю NextJS, но все же признаю, что в нем много крутых фич, о которых расскажу далее.
tailwindcss - 🔥 or 💩?

В последнее время часто встречаю посты о модном молодежном CSS-фреймворке tailwindcss (да что посты, уже вакансии появляются с «хорошо было бы знать»). Для тех, кто не знаком, это библиотека с огромным количеством написанных css-классов, используя которые в 99% случаев не нужно писать CSS вручную. На гитхабе у него аж 66 тысяч звездочек.

Но я не понимаю такого восторга. Огромное количество css-классов в коде (в className может прокидываться до 30-40 значений, если у вас несколько тем или адаптивная верстка). Высокий порог вхождения (там просто сотни классов, которые нереально помнить).

И самое больное (в сердечко) - это обезличенные компоненты. DOM-дерево превращается в портянку безликих дивов, даже в примерах на главной странице не всегда понятно, что в коде (карточка товара или слайдер, что за кнопка, блок и т.д.). Можете посмотреть примеры кода по ссылке https://tailwindcss.com . Кто бы как не относился к БЭМ-у, но он очень хорош в плане компонентной архитектуры CSS. Есть блок (= компонент), есть элементы блока и модификаторы. По исходному коду (или девтулзам) сразу понятно: футер (.footer), бургер меню в шапке (.header__burger-menu), сайдбар в раскрытом состоянии (.sidebar_opened).

Недавно посмотрел доклад с митапа по этой теме: https://www.youtube.com/watch?v=GMID4_t3EXU В презентации был выделен ряд проблем, включая те, которые я описал выше, а также возможные варианты их решения. Например, группировка классов по брейкпоинтам через classnames, использование test-id атрибутов для задания названий компонентов, инкапсуляция части стилей в свой класс для переиспользования. Еще подключить ESLint и VSCode плагины, чтобы было все по красоте. Посыл заключается в том, что нужно уметь правильно работать с tailwind и будет не все так плохо. Но зачем все это, когда можно использовать css-модули или тот же css-in-js?

Несмотря на мое отношение, я все же решил попробовать на пет проекте. Но когда не завелся плагин в VSCode и коллекция готовых модулей оказалась платная (хочешь шапку адаптивную или карточку товара - заплати 🤯), то вернулся к обычному css.

Если кто-то из чата уже использовал (или использует) tailwindcss на рабочих проектах, поделитесь своим опытом и фидбеком. Возможно нужно больше времени, чтобы все настроить и проникнуться идеей. Я пока придерживаюсь следующего – заверстать небольшой лендинг или пет-проджект – возможно подойдет. Но никак не для большого проекта.
Будущее React

Наткнулся на комментарий Дэна Абрамова в пул реквесте документации реакта, в котором популярный ютубер Тео Браун (t3gg) предложил заменить Create React App (CRA), инструмент для создания проекта на React от разработчиков самого реакта, на Vite для новых проектов.

Дэн подробно описал мнение команды о том, как они видят использование React в будущем. CRA был создан в качестве коробочного решения, которое (1) упрощает развертывание нового приложения на React и в котором (2) собраны лучшие практики DX (горячая перезагрузка, красный экран с ошибками в рантайме, простое обновление версий реакта). Все это сложно настраивается с нуля.

В настоящее время CRA в текущем виде умер (те, кто сидит в твиттере, наверняка видели твиты типа «CRA is dead»). Это произошло не из-за того, что под капотом используется webpack (медленный по сравнению с альтернативами), или из-за того, что за почти год в нем не было никаких правок и новых фич, несмотря на огромное количество ишьюсов (хотя из-за этого тоже). Просто команда реакта не видит в качестве целевой картины использование чистого React для новых приложений (только для ограниченного количества кейсов). Под чистым реактом я имею в виду client-only приложения, другими словами Single page application (SPA). А CRA умеет собирать только такие приложения. Дэн описал проблемы SPA: (1) долгая загрузка и белый экран, (2) размер бандла и (3) network waterfalls (когда первый запрос за данными может отправиться только после полной загрузки JS-кода).

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

И по итогу Дэн отмечает, что сейчас проблемы выше решаются на уровне фреймворков, которые так же предоставляют другие фичи (SSR или SSG, код-сплиттинг, (пред)загрузка данных, роутинг, навигация с сохранением клиентского UI стейта - лайауты и т.д.). А сам реакт будет помогать фреймворкам, развивая слой рендеринга (из последнего, это suspense, transition, серверные стриминг и компоненты). Теперь при старте проекта официально рекомендуется использовать именно один из фреймворков в режиме SSR/SSG и не использовать подход SPA (чистый реакт на том же Vite). А из CRA возможно сделают CLI-инструмент, который будет выбирать подходящий инструмент под вашу задачу.

Извините за такое количество аббревиатур, легко запутаться, но когда во фронтенде было по другому. Что забавно, CRA удалили из документации по реакту, Vite - не добавили. Сейчас там красуются NextJS, Remix и Gatsby (https://react.dev/learn/start-a-new-react-project).

Что касается SPA (на том же Vite), я кстати совсем не согласен, что это всегда хуже использования фреймворков, но об этом как-нибудь в следующий раз.
Будущее React (2)

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

Для тех, кто не читал предыдущий пост, то core-команда React-а задепрекейтила CRA (create react app) и думает развиваться в сторону UI-слоя для мета-фреймворков. В документации остались только мета-фреймворки (NextJS, Remix), и теперь там нет ни одного официального решения для запуска в рамках SPA.

Я вижу две ветки развития:

1⃣ Реакт развивается не как самостоятельный фреймворк, а как UI движок того же NextJS. Плюсы очевидны: фичи, над которыми команда разработчиков работала последние годы, будут полностью раскрыты, а так же решены проблемы SPA, что приведет к еще более производительному фронтенду. Не побоюсь сказать, к следующему этапу эволюции фронтенда, когда часть вычислений переносится на сервер. И несмотря на то, что я к этому скептически отношусь, это звучит многообещающе и довольно амбициозно.

Но есть и некоторые проблемы. Во-первых, резко вырастет кривая обучения до кривой мета-фреймворков. Сейчас для старта всего лишь нужно с помощью CRA/Vite развернуть проект и можно начинать знакомство. Зачем новичков погружать в SSR, RSC, особенности внешних решений? Часть людей это просто отпугнет, а у другой потеряется фокус на изучении именно реакта, и как с ним эффективно работать на стороне клиента (а реакт требует не мало ручных оптимизаций, чтобы быть производительным).

Во-вторых, сообщество разделится на два лагеря, поскольку большая часть разработчиков продолжат использовать реакт в качестве самостоятельного решения для SPA. И как следствие, часть туториалов/документации будет актуальна только для мета-фреймворков (например, серверные компоненты), другая - только для SPA приложений.

В-третьих, это зависимость от внешних решений. У руководства NextJS (Vercel) есть свои интересы. У них есть свои облачные сервера, на которых некст отлично работает. Я уже упоминал, как трудно запустить приложение на своих серверах (из-за закрытого API самого некста, плохой документации по серверной части и большой работе по настройке инфраструктуры). По сути рекомендованная схема будет такой: или используйте NextJS на серверах Vercel, или не используйте вовсе. Но разве это хорошо для опенсорс решения?

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

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

По какому пути пойдет команда реакта?
В прошлом месяце я начал работать над пет проектом на стеке React/Redux-Toolkit/Vite, используя методологию Feature Sliced (FSD). Вы можете посмотреть на него здесь: https://github.com/noveogroup-amorgunov/nukeapp

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

Сейчас я экспериментирую именно с FSD. Это архитектурная методология для фронтенда, которая говорит, как правильно должны быть расположены директории в проекте, а модули взаимодействовать друг с другом. Внутри себя FSD объединяет различные лучшие практики, такие как вертикальные слайсы, публичный API, композиция модулей, чистая архитектура, low coupling high cohesion. На мой взгляд это лучшее, что сейчас есть на рынке. Но по ряду причин методология часто используется не верно, а порой приносит больше вреда, чем пользы (мало примеров, много ситуаций, которые FSD никак не регламентирует, да и в общем архитектура - всегда сложно).

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

В общем, если вам будет интересно посмотреть на Feature Sliced в бою, то заглядывайте в репозиторий. Кстати, ссылку на проект добавили в примеры на официальный сайт методологии (https://feature-sliced.design/examples), поэтому проект уже принес небольшую пользу сообществу, пополнив шоукейс методологии.
Всем привет! С того момента, как я опубликовал предыдущий пост, количество звездочек у репозитория на гитхабе выросло на 140%, почти догнав все официальные примеры по Feature Sliced методологии. Спасибо за поддержку ❤️

Ниже хочу поделиться некоторыми техническими моментами, которые открыл для себя, делая этот пет проект:

- Насколько же приятно работать с бандлером Vite. У меня есть ассоциации с Vue (на котором я очень давно не писал, но это был очень приятный опыт с точки зрения разработки). Так вот так же и Vite, очень быстрый запуск, горячая перезагрузка, CLI стартер под нужный стек. Я ничего не могу сказать про большие проекты, на сколько в них Vite стабилен, но для небольших - работает отлично. Интересно в будущем попробовать SSR плагин в связке с React-ом.

- С помощью плагина eslint-plugin-boundaries можно (делать настоящую магию) задавать кастомные правила линтера для импортов внутри проекта. Для простых кейсов есть правило no-restricted-imports, для сложных - рекомендую именно этот плагин (на основе этого плагина сделано правило для импортов по FSD, которое я немного расширил для кросс-импортов в рамках слоя).

- Попробовал библиотеку zod для валидации runtime-структур. Описывайте схему объекта, с помощью которой можно и TypeScript тип вывести, и провалидировать данные. Можно проверять все то, что на момент компиляции типа unknown (переменные окружения, формы, параметры URL, ответы API и т.д.). Тоже рекомендую.

- Узнал, как работать с темным режимом. У меня на практике ни на одном проекте не было тем. Был приятно удивлен поддержкой браузеров (например, атрибут color-scheme), возможностями css-переменных для довольно простого переопределения цветовой палитры. А так же всяким UX штукам, что толщину шрифта на темных темах нужно делать меньше или накладывать фильтры на картинки, чтобы они были не такие яркие.

- И пару вещей про гитхаб: (1) бейджи для README можно генерировать в https://shields.io/ (куча интеграций и кастомные темы), (2) для светлой/темной тем можно отображать различные картинки в markdown файлах, добавив в ссылку на изображение специальный хеш: #gh-dark-mode-only или #gh-light-mode-only (подробнее в блоге самого гитхаба: https://github.blog/changelog/2021-11-24-specify-theme-context-for-images-in-markdown/).
ts-reset

Вы знали, что в TypeScript-е методы JSON.parse и response.json() внутри fetch по умолчанию неявно возвращают тип any? Мы и правда не можем знать, какая структура данных находится в строке или придет с бекенда в рантайме. Но вместо того, что вернуть unknown и явно сказать, что тип неизвестен, TypeScript для поддержки обратной совместимости возвращает any.

И таких небольших моментов довольно много: filter(Boolean) не сузит тип, Array.isArray или Array.from так же вернут any[], многие методы массива (includes, indexOf) будут некорректно работать с const assertions.

Мэтт Покок (на ютубе ведет канал про TypeScript) собрал такие кейсы в виде небольшой библиотеки ts-reset, которую можно подключить на свой проект в виде деклараций типов (d.ts файл). Все, что делает библиотека, переопределяет встроенные в TS интерфейсы (declaration merging). В issues можно посмотреть еще на ряд случаев, когда TypeScript плохо или странно выводит типы.

Подключив ts-reset к проекту, вы вряд ли получите какой-то вау эффект, но определенно сделаете типы на встроенные интерфейсы в языке лучше.
Валидация переменных окружения

Валидацией форм или параметров запроса никого не удивить, а вот на переменные окружения часто забивают и используют их напрямую. В чем проблема такого подход? Все переменные окружения в нашем коде будут типа string или undefined, даже если переменная описывает какой-нибудь булеан флаг или число. Поэтому часто в коде можно встретить конструкции типа process.env.IS_ENABLED === 'true' (здесь true - это строка) или parseInt(process.env.DELAY_MS, 10). И в каждом месте использования нужно не забыть сделать такую проверку.

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

1. Все переменные можно считывать в одном месте (например, в каком-нибудь src/env.ts файле), трансформировать/валидировать данные под нужный формат, а во всех остальных местах использовать уже подготовленные переменные. Как бонус можно запретить использование process.env.* за пределами этого файла с помощью eslint.

2. Я недавно писал про библиотеку zod, которая позволяет описывать схемы валидации. Она также позволяет трансформировать данные, а в одном из последних релизов появилась возможность приведения (coerce) примитивных типов. Например, в процессе валидации преобразовать входные данные к нужному примитивному типу (в нашем случае для переменных окружений, которые представляют собой числовое значение, преобразовать строку в число). Эта библиотека отлично решают задачу подготовки переменных.

Что нам дают эти два пункта? Приводим переменные окружения к нужному типу, валидируем их и не даем запустить проект при отсутствии обязательных.

Пример валидации env переменных с помощью zod можно посмотреть тут.
Всем привет! На выходных дописал пост в блог про архитектуру проектов на основе вертикальных слайсов: https://amorgunov.com/posts/2023-05-28-vertical-sliced-architecture-in-frontend/

Получилось немного сумбурно, но по итогу удалось сформировать универсальную структуру фронтенд проекта. Сейчас нет смысла собирать структуру с нуля (можно использовать тот же Feature Sliced), однако понимать архитектурные подходы, которые используются внутри, нужно. Особенно, если вы запускаете новые проекты или занимаетесь рефакторингом текущих (или планируете это делать в будущем). Вертикальные слайсы - один из таких подходов.
Друзья, всем привет! Почти год ничего не писал, и за это время накопилось большое количество тем, которые хочется с вами обсудить. Пора оживлять канал, поэтому в ближайшее время ждите посты про NextJS (RSC и React конечно же), про Feature-Sliced Design, про фронтенд платформу, про обзоры технических книг, которые я прочитал за последнее время и про многое другое, что каким-то образом связано с разработкой.

Для новеньких буквально пару слов о себе (а таких за год появилось довольно много, за что отдельное спасибо Виталию @web_platform, который собрал папку с фронтенд группами и добавил этот канал, вас стало чуть ли не в два раза больше). Меня зовут Александр, живу в Питере, работаю над клиентским веб приложением Самоката в роли техлида. До этого работал в Яндексе и аутсорсе. Иногда выступаю с докладами, периодически занимаюсь менторингом (много лет назад в Яндексе даже офлайн курс по фронтенду организовывал). Здесь публикую контент для уровня мидл и выше, совсем о базе почти не пишу. В любом случае всем рад, добро пожаловать!)

Кстати, на этих выходных (уже завтра) буду на конференции CodeFest в Новосибирске, после сделаю выжимку самых интересных докладов, которые получится послушать. Если вдруг тоже будете там, подходите пообщаться, большую часть времени буду у стенда Samokat_tech.
В марте выступал на MoscowJS и рассказывал про NextJS, о чем стоит знать, если вы хотите использовать его в своем проекте. Доклад всего на 20 минут, но краткую выжимку сделаю ниже.

Что важно знать про NextJS? У него сейчас доступны две архитектуры для разработки: Page Router (архитектура, реализующая стандартный SSR подход, которая считается устаревшей, но формально поддерживаемая разработчиками, на самом деле нет) и пришедшая на замену - App Router (доступна с 13 версии, вносит кучу новых фич, типа Streaming render, RSC, selective hydration, partial prerendering, все то, что было недоступно ранее). Эти архитектуры разделяют NextJS на два разных фреймворка (даже вся документация разбита на два независимых раздела).

Далее рассказывал про темные стороны (проблемы):

1️⃣ Серверная составляющая. Довольно важная тема, которую редко обсуждают - конфигурируемость и гибкость сервера, чем NextJS не может похвастаться. Нельзя без манки-патчинга собирать логи, без кастомного сервера собирать http-метрики (и даже с ним внутренние метрики рендеринга остаются недоступными), нет никаких инструментов для борьбы с отказоустойчивостью и работы с кэшированием. Получаем черную коробку с очень скудным публичным API для работы с сервером.

Справедливости ради, в App Router есть возможность работать с кэшем, но без него (и с RSC) приложение выжирало бы абсолютно все ресурсы серверов даже при небольшой нагрузке.

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

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

2️⃣ Вендорлок. У сообщества создалось впечатление, что разработчики NextJS разрабатывают и проектируют фичи в первую очередь под запуск на серверах Vercel (логично, компания этим зарабатывает деньги), и уже после для запуска на своей инфраструктуре. А деплой на своей инфре из-за «бедного» сервера совсем не тривиальный. И все бы ничего, но одновременно React сильно привязался к NextJS. Многие последние фичи реакта поддержаны именно в нексте, что создает неявную связь, хочешь использовать реакт, будь готов деплоиться на Vercel.

3️⃣ Будущее. Тут я рассказывал про RSC (серверные компоненты реакта), о которых уже писал ранее. Чтобы их использовать (доступны только в App Router), нужно сильно переписать проект, если он изначально был на Page Router, из-за чего многие отказываются мигрировать (мы у себя тоже пока решили жить на старой архитектуре). И здесь проблема в том, что Page Router тихо перестают поддерживать и развивать, а многие ишьюсы просто закрывают с комментами "решено в App Router - используйте его".

А если проект начинать на App Router, то имеем другую пачку открытых ишьюсов уже в App Router, в части которых ребята из кортимы рекомендуют использовать Page Router (например баг с useParams в режиме SSG). Получаем замкнутый круг.

Какое дальнейшее развитие я вижу? Разработчики NextJS не смогут усидеть на двух стульях и поддерживать две архитектуры, и от App Router отказываться точно не будут. Поэтому первое, что сейчас важно сделать команде некста, это активно закрывать баги в новой архитектуре и в какой-то момент официально задепрекейтить старую (это точно негативно воспримет часть сообщества, но других вариантов я не вижу). И второе, работать над расширяемостью и кастомизацией серверной части, чтобы уменьшить поток критики по поводу вендорлок-стратегии.

Бонусом собрал немного полезного материала про NextJS, который использовал в ходе подготовки к докладу.
Завтра буду выступать на конференции «Я люблю фронтенд» от Яндекса и рассказывать про архитектурную методологию Feature-Sliced Design. Сделаю общий экскурс, разберу основные понятия, подробно остановлюсь на нескольких слоях (сущности, фичи и виджеты). Разберу проблемы, с которыми столкнулся на рабочем проекте: взаимодействие модулей на разных уровнях, кросс-импорты и нехватка слоев. Для каждой проблемы конечно же расскажу о возможных вариантах решения.

Если вы тесно работаете с методологией, то я вряд ли открою для вас что-то новое. Но если вы только планируете использовать FSD или просто тема интересна для вас, то присоединяйтесь к трансляции на ютубе. Сама конференция будет с 11:00 до 18:00, я начну вещать с 13:15 по МСК (запись тоже будет доступна).

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