Привет!
Про выход tramvai в open source.
Зачем это нужна для команды разработчиков, и для Тинкофф в целом?
Одна из важнейших вещей, это еще одно громкое заявление - "В Тинькофф крутой фронтенд!"
Разработчики Тинькофф делают все больше отличных open source проектов, пишут все больше интересных статей, выступают на конференциях - из таких активностей формируется образ компании, привлекательный для новых разработчиков.
Мы активно нанимаем коллег, и у нас классные интервьюеры 😉
Для мейнтейнеров tramvai, это новый источник вдохновения для развития фреймворка, возможность получить свежий взгляд со стороны.
И один из плюсов, это быстрое прототипирование с помощью JS песочниц, недавно мы добавили базовый шаблон трамвая на Codesandbox - https://codesandbox.io/s/tramvai-new-qgk90
Далее, опишу ряд проблем, которые могут стоять на пути у проприетарной разработки, усложняющие переход в OSS:
- Код, документация и история VCS могут содержать чувствительную информацию - самый простой пример, это ссылки на внутренние ресурсы
- Часть пакетов подходит для OSS, часть предназначена только для внутреннего использования
- Пакеты публикуются в приватный регистр пакетов
- Проект использует приватный CI
- Отсутствут локализация документации
Таким образом, полный переход Github требует отделить публичную часть проекта, перенести код без сохранения истории коммитов, настроить в Github Actions линтинг, unit и интеграционное тестирование, версионирование и публикацию пакетов, перевод, сборку и деплой документации.
Например, такой переход осуществил огненный Angular ui-kit от наших коллег - https://taiga-ui.dev/
Taiga содержит ряд проприетарных компонентов в приватном репозитории внутри Тинькофф, и приватный репозитрий использует публичный с помощью git submodules.
Почему этот путь не подошел для tramvai:
- Теряется история коммитов - это важный источник информации для разработчика
- Проект имеет достаточно сложный CI, и в версионировании и публикации пакетов участвует другая внутренняя разработка Тинькофф, о которой я писал в одном из предыдущих постов
- Все tramvai пакеты объединены сквозным версионированием, отделение публичных означает, что два набора пакетов разойдутся в версиях, и вернутся проблемы, связанные с обновлением зависимостей
- Не удобно делать глобальный рефакторинг, который затрагивает все пакеты
Каждая из этих проблем кажется неизбежным злом, но вместе порождают очень много сложностей, которые мы постарались решить инкрементальным выходом в OSS.
Про выход tramvai в open source.
Зачем это нужна для команды разработчиков, и для Тинкофф в целом?
Одна из важнейших вещей, это еще одно громкое заявление - "В Тинькофф крутой фронтенд!"
Разработчики Тинькофф делают все больше отличных open source проектов, пишут все больше интересных статей, выступают на конференциях - из таких активностей формируется образ компании, привлекательный для новых разработчиков.
Мы активно нанимаем коллег, и у нас классные интервьюеры 😉
Для мейнтейнеров tramvai, это новый источник вдохновения для развития фреймворка, возможность получить свежий взгляд со стороны.
И один из плюсов, это быстрое прототипирование с помощью JS песочниц, недавно мы добавили базовый шаблон трамвая на Codesandbox - https://codesandbox.io/s/tramvai-new-qgk90
Далее, опишу ряд проблем, которые могут стоять на пути у проприетарной разработки, усложняющие переход в OSS:
- Код, документация и история VCS могут содержать чувствительную информацию - самый простой пример, это ссылки на внутренние ресурсы
- Часть пакетов подходит для OSS, часть предназначена только для внутреннего использования
- Пакеты публикуются в приватный регистр пакетов
- Проект использует приватный CI
- Отсутствут локализация документации
Таким образом, полный переход Github требует отделить публичную часть проекта, перенести код без сохранения истории коммитов, настроить в Github Actions линтинг, unit и интеграционное тестирование, версионирование и публикацию пакетов, перевод, сборку и деплой документации.
Например, такой переход осуществил огненный Angular ui-kit от наших коллег - https://taiga-ui.dev/
Taiga содержит ряд проприетарных компонентов в приватном репозитории внутри Тинькофф, и приватный репозитрий использует публичный с помощью git submodules.
Почему этот путь не подошел для tramvai:
- Теряется история коммитов - это важный источник информации для разработчика
- Проект имеет достаточно сложный CI, и в версионировании и публикации пакетов участвует другая внутренняя разработка Тинькофф, о которой я писал в одном из предыдущих постов
- Все tramvai пакеты объединены сквозным версионированием, отделение публичных означает, что два набора пакетов разойдутся в версиях, и вернутся проблемы, связанные с обновлением зависимостей
- Не удобно делать глобальный рефакторинг, который затрагивает все пакеты
Каждая из этих проблем кажется неизбежным злом, но вместе порождают очень много сложностей, которые мы постарались решить инкрементальным выходом в OSS.
Первый этап, на котором сейчас находится проект - это зеркалирование публичного кода на Github, при этом вся разработка ведется в приватном репозитории.
На этапе исследования, мы нашли мощный инструмент, созданный для аналогичных целей внутри Google,
При чем, кажется это вторая или третья итерация по созданию такого инструмента от разработчиков Google.
Кстати, для copybara есть гибкая обвязка под Github Actions - https://github.com/Olivr/copybara-action и публичный Docker образ - https://github.com/anipos/copybara-docker-image
Каждый релиз трамвая, запускается джоба с генерацией конфига и запуском copybara.
Конфиг содержит список публичных файлов и трансформации для кода - например, замена ссылок приватной документации на https://tramvai.dev
Copybara проверяет изменения с последнего коммита в Github репе, схлопывает все новые коммиты в один релизный коммит, и пушить его на Github.
Затем в Github Actions запускается сборка и деплой документации, и сборки и публикация пакетов в npm.
Я несколько раз накосячил с публикацией пакетов и откатывал их, оказалось, что npm запрещает публикацию удаленных пакетов в течение 24 часов, и запрещает публиковать ту же версию, что была удалена 😥
По итогу, мы всегда имеем актуальный код на Github, наш фреймворк доступен для публичного использования, и при этом наши процессы разработки не поменялись.
Следующий шаг миграции в OSS - это зеркалирование MR от контрибьюторов из Github в наш приватный репозиторий, на данный момент мы можем делать это только вручную.
Двусторонняя синхронизация репозиториев это потенциальный источник проблем, поэтому мы не спешим с переходом на этот этап.
В будущем хочется полностью перейти на Github, отказавшись от синхронизации репозиториев.
На этапе исследования, мы нашли мощный инструмент, созданный для аналогичных целей внутри Google,
copybara - https://github.com/google/copybara.При чем, кажется это вторая или третья итерация по созданию такого инструмента от разработчиков Google.
Кстати, для copybara есть гибкая обвязка под Github Actions - https://github.com/Olivr/copybara-action и публичный Docker образ - https://github.com/anipos/copybara-docker-image
Каждый релиз трамвая, запускается джоба с генерацией конфига и запуском copybara.
Конфиг содержит список публичных файлов и трансформации для кода - например, замена ссылок приватной документации на https://tramvai.dev
Copybara проверяет изменения с последнего коммита в Github репе, схлопывает все новые коммиты в один релизный коммит, и пушить его на Github.
Затем в Github Actions запускается сборка и деплой документации, и сборки и публикация пакетов в npm.
Я несколько раз накосячил с публикацией пакетов и откатывал их, оказалось, что npm запрещает публикацию удаленных пакетов в течение 24 часов, и запрещает публиковать ту же версию, что была удалена 😥
По итогу, мы всегда имеем актуальный код на Github, наш фреймворк доступен для публичного использования, и при этом наши процессы разработки не поменялись.
Следующий шаг миграции в OSS - это зеркалирование MR от контрибьюторов из Github в наш приватный репозиторий, на данный момент мы можем делать это только вручную.
Двусторонняя синхронизация репозиториев это потенциальный источник проблем, поэтому мы не спешим с переходом на этот этап.
В будущем хочется полностью перейти на Github, отказавшись от синхронизации репозиториев.
GitHub
GitHub - google/copybara: Copybara: A tool for transforming and moving code between repositories.
Copybara: A tool for transforming and moving code between repositories. - google/copybara
😁1
Привет!
Хочу рассказать про одно не законченное исследование, и возможно таким образом дать себе инерцию завершить это дело)
Фронтенд экосистема Тинькофф состоит из двух мощных экосистем - Angular и React, примерно 100+ разработчиков на каждый фреймворк.
Для каждой экосистемы разработан и поддерживается свой ui-kit - один для React, и второй для Angular.
Целью исследования было проверить, можно ли создать функциональный фреймворк-агностик ui-kit на одной технологии, и эффективно переиспользовать его в каждом нашем фреймворке.
Спойлер - реализации прототипа только на одной технологии мне хватило для вывода, что реализовать такой kit, не потеряв при его использовании developer experience, сравнительно с нативным китом, невозможно, или крайне сложно, но мне по прежнему очень интересно, чего можно добиться с различными технологиями.
Начнем со списка возможностей, который дает нам React UI-kit в React приложении:
- Можно передавать свойства в компонент
- Обновление свойств приведет к обновлению DOM
- В компонент можно вкладывать другие компоненты (композиция с помощью children и render props)
- Доступ к context приложения
- Поддержка SSR
Начать исследования я решил со знакомых, и на первый взгляд максимально близкий друг к другу технологий - React приложение и Preact UI-kit.
Другие потенциальные технологии - Web Components (плюс один из фреймворков с поддержкой SSR), Svelte.
Для интеграции Preact компонентов (да и любых других в будущем) в React приложение я решил использовать концепцию адаптеров - такой адаптер принимает Preact компонент из кита, и возвращает React компонент, готовый к использованию в приложении.
Код адаптера доступен тут - https://github.com/SuperOleg39/agnostik-kit/blob/master/adapters/preact-to-react/src/index.js
Для проверки возможностей, заранее создал адаптер-пустышку, обернул в него компоненты, и написал в приложении основные кейсы - с передачей пропсов, с коллбэками, пробросом в children текста, React компонентов, и других Preact компонентов, обернутых в адаптер.
Кейсы можно посмотреть тут - https://github.com/SuperOleg39/agnostik-kit/blob/master/apps/react-app/src/App.js
Рассмотрим подробнее реализацию адаптера.
На серверной стороне, мы должны отрендерить Preact компонент в строку c помощью
Вспомним требование про композицию - в children могут быть React компоненты, которые заранее надо отрендерить в строку, и так же вставить в китовый компонент.
React компоненты могут содержать другие PreactAdapter компоненты, так что по сути это рекурсивный процесс.
Оказывается, что механизм добавления произвольной разметки в React и Preact одинаковый -
Это дает нам возможность композиции, в несколько этапов:
1. Делаем ReactDOM.renderToString текущих children
2. Результат передаем в
3. Делаем Preact.renderToString этого компонента
4. Результат передаем в
В клиентском коде, в этот враппер необходимо отрендерить Preact компонент, а при наличии children, сделать их гидрацию (помним, что там будут React компоненты), используя ref на китовый компонент в качестве root.
Также, необходимо делать повторный рендеринг Preact компонента, при каждом изменении props.
Такой небольшой адаптер покрывает большую часть полноценной интеграции, и мы уже можем увидеть ряд недостатков:
- Теряем react context для вложенных React компонентов
- DOM дерево разрастается из-за врапперов для
- Нарушаем флоу работы React - вместо стандартного рендеринга дерева компонентов от самого вложенного, к родителю, и общего маунта, мы рендерим компоненты в адаптере сверху вниз, и маунтим по цепочке.
В теории, при больших поддеревьях компонентов, можно увидеть значительные задержки рендеринга.
- Нужно писать надежный механизм перерендеринга китового компонента
Хочу рассказать про одно не законченное исследование, и возможно таким образом дать себе инерцию завершить это дело)
Фронтенд экосистема Тинькофф состоит из двух мощных экосистем - Angular и React, примерно 100+ разработчиков на каждый фреймворк.
Для каждой экосистемы разработан и поддерживается свой ui-kit - один для React, и второй для Angular.
Целью исследования было проверить, можно ли создать функциональный фреймворк-агностик ui-kit на одной технологии, и эффективно переиспользовать его в каждом нашем фреймворке.
Спойлер - реализации прототипа только на одной технологии мне хватило для вывода, что реализовать такой kit, не потеряв при его использовании developer experience, сравнительно с нативным китом, невозможно, или крайне сложно, но мне по прежнему очень интересно, чего можно добиться с различными технологиями.
Начнем со списка возможностей, который дает нам React UI-kit в React приложении:
- Можно передавать свойства в компонент
- Обновление свойств приведет к обновлению DOM
- В компонент можно вкладывать другие компоненты (композиция с помощью children и render props)
- Доступ к context приложения
- Поддержка SSR
Начать исследования я решил со знакомых, и на первый взгляд максимально близкий друг к другу технологий - React приложение и Preact UI-kit.
Другие потенциальные технологии - Web Components (плюс один из фреймворков с поддержкой SSR), Svelte.
Для интеграции Preact компонентов (да и любых других в будущем) в React приложение я решил использовать концепцию адаптеров - такой адаптер принимает Preact компонент из кита, и возвращает React компонент, готовый к использованию в приложении.
Код адаптера доступен тут - https://github.com/SuperOleg39/agnostik-kit/blob/master/adapters/preact-to-react/src/index.js
Для проверки возможностей, заранее создал адаптер-пустышку, обернул в него компоненты, и написал в приложении основные кейсы - с передачей пропсов, с коллбэками, пробросом в children текста, React компонентов, и других Preact компонентов, обернутых в адаптер.
Кейсы можно посмотреть тут - https://github.com/SuperOleg39/agnostik-kit/blob/master/apps/react-app/src/App.js
Рассмотрим подробнее реализацию адаптера.
На серверной стороне, мы должны отрендерить Preact компонент в строку c помощью
preact-render-to-string, до рендеринга в строку нашего React приложения.Вспомним требование про композицию - в children могут быть React компоненты, которые заранее надо отрендерить в строку, и так же вставить в китовый компонент.
React компоненты могут содержать другие PreactAdapter компоненты, так что по сути это рекурсивный процесс.
Оказывается, что механизм добавления произвольной разметки в React и Preact одинаковый -
dangerouslySetInnerHTML: { __html: string }, правда нам потребуется создавать лишний тег - враппер, который и будет содержать эту разметку.Это дает нам возможность композиции, в несколько этапов:
1. Делаем ReactDOM.renderToString текущих children
2. Результат передаем в
dangerouslySetInnerHTML Preact компонента3. Делаем Preact.renderToString этого компонента
4. Результат передаем в
dangerouslySetInnerHTML враппераВ клиентском коде, в этот враппер необходимо отрендерить Preact компонент, а при наличии children, сделать их гидрацию (помним, что там будут React компоненты), используя ref на китовый компонент в качестве root.
Также, необходимо делать повторный рендеринг Preact компонента, при каждом изменении props.
Такой небольшой адаптер покрывает большую часть полноценной интеграции, и мы уже можем увидеть ряд недостатков:
- Теряем react context для вложенных React компонентов
- DOM дерево разрастается из-за врапперов для
dangerouslySetInnerHTML- Нарушаем флоу работы React - вместо стандартного рендеринга дерева компонентов от самого вложенного, к родителю, и общего маунта, мы рендерим компоненты в адаптере сверху вниз, и маунтим по цепочке.
В теории, при больших поддеревьях компонентов, можно увидеть значительные задержки рендеринга.
- Нужно писать надежный механизм перерендеринга китового компонента
GitHub
agnostik-kit/adapters/preact-to-react/src/index.js at master · SuperOleg39/agnostik-kit
Contribute to SuperOleg39/agnostik-kit development by creating an account on GitHub.
Взвесив все эти минусы интеграции таких близких на первый взгляд технологий, решили не продолжать исследования, но идея попробовать другие технологии осталась.
Через несколько месяцев, мне попалась статья на habr про создание общего UI-kit для Vue и React с помощью Stencil - https://habr.com/ru/company/uchi_ru/blog/543308/
Веб-компоненты в связке со Stencil кажутся перспективными для фреймворк агностик кита, из-за флоу инициализации и работы на сервере и клиенте - рендеринг web components происходит поверх уже отрендеренного приложения, что в теории позволяет сохранить общее дерево нашего React приложения.
А внешняя схожесть React и Preact на самом деле никак не упрощает интеграцию, из-за завязки рендеринга фреймворка на конкретный DOM узел.
Также, у Stencil существует интеграция для React - https://stenciljs.com/docs/react
Про не завершенную, но интересную попытку интегрировать Stencil постараюсь рассказать в следующем посте.
Через несколько месяцев, мне попалась статья на habr про создание общего UI-kit для Vue и React с помощью Stencil - https://habr.com/ru/company/uchi_ru/blog/543308/
Веб-компоненты в связке со Stencil кажутся перспективными для фреймворк агностик кита, из-за флоу инициализации и работы на сервере и клиенте - рендеринг web components происходит поверх уже отрендеренного приложения, что в теории позволяет сохранить общее дерево нашего React приложения.
А внешняя схожесть React и Preact на самом деле никак не упрощает интеграцию, из-за завязки рендеринга фреймворка на конкретный DOM узел.
Также, у Stencil существует интеграция для React - https://stenciljs.com/docs/react
Про не завершенную, но интересную попытку интегрировать Stencil постараюсь рассказать в следующем посте.
Хабр
Единый UI-кит и синхронизация дизайна в Учи.ру. Часть 1
Пожалуй, все, кто имел дело с развитием семейства сайтов, сталкивались с проблемой поддержания единого вида компонентов. Когда счет сервисов идет на десятки и со...
Привет!
Продолжаю тему создания фреймворк-агностик UI-kit, с помощью веб-компонентов и Stencil - https://stenciljs.com/
Почему нам вообще нужно использовать какой-то фреймворк для веб-компонентов?
Для этого есть ряд причин, вот несколько самых важных:
- web components не поддерживают server-side rendering
- механизм обновления DOM должен реализовывать разработчик
- много шаблонного кода
Фреймворк
Отдельная боль - это не прозрачная конфигурация, и отсутствие рецептов в документации, как интегрировать Stencil, например новый UI-kit, в уже существующую монорепу.
Пока писал этот пост, обнаружил что во время предыдущего исследования сделал не корректную интеграцию, генерируя Stencil код не в библиотеку с компонентами, а в само приложение - https://github.com/SuperOleg39/agnostik-kit/pull/1
Нашел на гитхабе хороший пример интеграции Stencil и NextJS - https://github.com/jagreehal/nextjs-stenciljs-ssr-example, его и буду использовать для нового исследования.
Вот так выглядит рендеринг в строку на сервере:
И гидрация на клиенте:
Пример достаточно старый, и не использует официальные биндинги для React - https://stenciljs.com/docs/react, вместо этого используется хук
Сгенерировал биндинги в этом PR - https://github.com/SuperOleg39/nextjs-stenciljs-ssr-example/pull/1, большая часть кода - генерируется Stencil.
Интеграция React биндингов тоже крайне не привычна - мы должны создать пакет - "пустышку" для будущих React компонентов, но при этом компилировать его содержимое будет наш пакет с web компонентами, везде сложно понять, что надо сохранять в git, а что нужно только для публикации.
Еще заметил минус у биндингов - нет поддержки SSR при генерации React компонентов, и отсутствует нужный рецепт в документации, по итогу надо использовать оба пакета - с web-компонентами и с React биндингами.
Краткое резюме, после базовой интеграции Stencil с React и NextJS:
сама интеграция - достаточно проблемная, но мы получили типизированные React компоненты и возможность передавать в свойста веб-компонентов children, функции и объекты.
children можно передавать, если в веб-компоненте используется
Далее, я начал проверять, как ведут себя
Кстати, в React биндингах Stencil есть хак -
Затем, я провел ряд экспериментов, где в web-component вставлял React ноды, другие web-components, PR с тестовыми кейсами можно посмотреть тут (файл `a.tsx`) - https://github.com/SuperOleg39/nextjs-stenciljs-ssr-example/pull/2/files
Продолжаю тему создания фреймворк-агностик UI-kit, с помощью веб-компонентов и Stencil - https://stenciljs.com/
Почему нам вообще нужно использовать какой-то фреймворк для веб-компонентов?
Для этого есть ряд причин, вот несколько самых важных:
- web components не поддерживают server-side rendering
- механизм обновления DOM должен реализовывать разработчик
- много шаблонного кода
Stencil решает эти проблемы, предоставляя поддержку SSR, vDOM + реактивные биндинги, и возможность использовать JSXФреймворк
Stencil дает много возможностей, хотя оказался для меня достаточно не комфортным в интеграции - дело в том, что аналогично Svelte, Stencil это компилируемый фреймворк, и например создавая библиотеку компонентов, перед публикацией мы должны генерировать код для рендеринга этих компонентов, который будет использовать наше приложение.Отдельная боль - это не прозрачная конфигурация, и отсутствие рецептов в документации, как интегрировать Stencil, например новый UI-kit, в уже существующую монорепу.
Пока писал этот пост, обнаружил что во время предыдущего исследования сделал не корректную интеграцию, генерируя Stencil код не в библиотеку с компонентами, а в само приложение - https://github.com/SuperOleg39/agnostik-kit/pull/1
Нашел на гитхабе хороший пример интеграции Stencil и NextJS - https://github.com/jagreehal/nextjs-stenciljs-ssr-example, его и буду использовать для нового исследования.
Вот так выглядит рендеринг в строку на сервере:
// под капотом у renderToHTML - ReactDOM.renderToString
const html = await app.renderToHTML(req, res, req.path, req.query);
// рендерим Stencil поверх результата рендеринга ReactDOM
const renderedHtml = await stencil.renderToString(html);
И гидрация на клиенте:
const {
applyPolyfills,
defineCustomElements
} = require("stencil-web-components/loader");
// все это происходит после ReactDOM.hydrate
applyPolyfills().then(() => {
defineCustomElements(window);
});
Пример достаточно старый, и не использует официальные биндинги для React - https://stenciljs.com/docs/react, вместо этого используется хук
useCustomElementСгенерировал биндинги в этом PR - https://github.com/SuperOleg39/nextjs-stenciljs-ssr-example/pull/1, большая часть кода - генерируется Stencil.
Интеграция React биндингов тоже крайне не привычна - мы должны создать пакет - "пустышку" для будущих React компонентов, но при этом компилировать его содержимое будет наш пакет с web компонентами, везде сложно понять, что надо сохранять в git, а что нужно только для публикации.
Еще заметил минус у биндингов - нет поддержки SSR при генерации React компонентов, и отсутствует нужный рецепт в документации, по итогу надо использовать оба пакета - с web-компонентами и с React биндингами.
Краткое резюме, после базовой интеграции Stencil с React и NextJS:
сама интеграция - достаточно проблемная, но мы получили типизированные React компоненты и возможность передавать в свойста веб-компонентов children, функции и объекты.
children можно передавать, если в веб-компоненте используется
<slot />Далее, я начал проверять, как ведут себя
children в веб-компоненте, и обнаружил, что Stencil по умолчанию не использует shadow DOM, и все веб-компоненты, используемые как React биндинги, сломаны на этапе ReactDOM.hydrate - для исправления проблемы нужно включать shadow DOM вручную для каждого Stencil компонента, через shadow: true, немного о проблеме в этой статье - https://leechy.dev/hide-stencil-childrenКстати, в React биндингах Stencil есть хак -
defineCustomElements вызывается сразу, т.е. до ReactDOM.hydrate - а React уже получает shadow DOM при гидрации, и не модифицирует его.Затем, я провел ряд экспериментов, где в web-component вставлял React ноды, другие web-components, PR с тестовыми кейсами можно посмотреть тут (файл `a.tsx`) - https://github.com/SuperOleg39/nextjs-stenciljs-ssr-example/pull/2/files
Stencil
Build. Customize. Distribute. Adopt.
Я был настроен скептически, но все тесты показали отличный результат:
- Сам механизм интеграции веб-компонентов устроен так, что React context не теряется
- Рендеринг на сервере и гидрация - работают корректно
- React прекрасно перерендеривает children, которые веб-компоненты рендерят через
- Веб-компоненты прекрасно обрабатывают изменение локального состояния и shadow DOM, не ломая управляемую React`ом разметку, которая приходит через
- Многократная вложенность React и веб-компонентов друг в друга ничего не ломают
Все работает корректно, если я все правильно понял, т.к. React управляет только разметкой в
Завершая исследование, могу сказать что web-components действительно отлично подходят для создания компонентов, которые могут быть использованы в любом фреймворке.
Мейнтейнеры
Для SPA приложений, интеграция веб-компонентов тривиальна, и может даже не требовать отдельных фреймворков вроде
Какие вопросы у меня остаются к
- Документация и интеграции - очень много открытых вопросов, интеграция с React рассмотрена поверхностно
- Размер генерируемого кода - для нескольких компонентов, Stencil сгенерировал 400кб исходного кода. Сделал анализа бандла, импорт и использование Stencil компонента из нашего UI-kit добавляет примерно 45кб gzip кода.
Определенно, такое маленькое приложение на Svelte сгенерировало бы гораздо меньше кода, это примерно размер react + react-dom
- Производительность рендеринга - сделал поверхностные замеры, похоже и на сервере и на клиенте все быстро - но приложение слишком небольшое
- Необходимость в React биндингах - очень уж не очевидный механизм для их генерации
- Сам механизм интеграции веб-компонентов устроен так, что React context не теряется
- Рендеринг на сервере и гидрация - работают корректно
- React прекрасно перерендеривает children, которые веб-компоненты рендерят через
<slot />, не ломая разметку веб-компонента- Веб-компоненты прекрасно обрабатывают изменение локального состояния и shadow DOM, не ломая управляемую React`ом разметку, которая приходит через
children- Многократная вложенность React и веб-компонентов друг в друга ничего не ломают
Все работает корректно, если я все правильно понял, т.к. React управляет только разметкой в
template, а веб-компоненты сразу рендерят эти template в slot, т.е. области ответственности фреймворков не пересекаются.Завершая исследование, могу сказать что web-components действительно отлично подходят для создания компонентов, которые могут быть использованы в любом фреймворке.
Мейнтейнеры
Stencil проделали огромную работу, и фреймворк дает нам возможность использовать компоненты в SSR React приложении, без каких-то серьезных ограничений в developer experience.Для SPA приложений, интеграция веб-компонентов тривиальна, и может даже не требовать отдельных фреймворков вроде
Stencil или lit-element для простых случаев.Какие вопросы у меня остаются к
Stencil:- Документация и интеграции - очень много открытых вопросов, интеграция с React рассмотрена поверхностно
- Размер генерируемого кода - для нескольких компонентов, Stencil сгенерировал 400кб исходного кода. Сделал анализа бандла, импорт и использование Stencil компонента из нашего UI-kit добавляет примерно 45кб gzip кода.
Определенно, такое маленькое приложение на Svelte сгенерировало бы гораздо меньше кода, это примерно размер react + react-dom
- Производительность рендеринга - сделал поверхностные замеры, похоже и на сервере и на клиенте все быстро - но приложение слишком небольшое
- Необходимость в React биндингах - очень уж не очевидный механизм для их генерации
Интересная статья про минусы
Но похоже это единственное хорошее решение с поддержкой SSR, в
Stencil - https://www.abeautifulsite.net/posts/moving-from-stencil-to-lit-element/Но похоже это единственное хорошее решение с поддержкой SSR, в
lit это только в экспериментах/планах - https://www.polymer-project.org/blog/2020-09-22-lit-element-and-lit-html-next-preview и https://github.com/lit/lit/tree/main/packages/labs/ssrwww.abeautifulsite.net
Moving from Stencil to LitElement
A blog about everything web. Est. 2007
В чате Reatom увидел очень интересную библиотеку Mitosis - https://github.com/BuilderIO/mitosis
Позволяет писать компоненты на синтаксисе, близком к React и Solid, и компилировать их в нативные компоненты на большинстве фреймворков.
Ещё один потенциальный кандидат на инструмент для создания фреймворк-агностик UI-kit
Позволяет писать компоненты на синтаксисе, близком к React и Solid, и компилировать их в нативные компоненты на большинстве фреймворков.
Ещё один потенциальный кандидат на инструмент для создания фреймворк-агностик UI-kit
GitHub
GitHub - BuilderIO/mitosis: Write components once, run everywhere. Compiles to React, Vue, Qwik, Solid, Angular, Svelte, and more.
Write components once, run everywhere. Compiles to React, Vue, Qwik, Solid, Angular, Svelte, and more. - GitHub - BuilderIO/mitosis: Write components once, run everywhere. Compiles to React, Vue, ...
SuperOleg dev notes pinned «Привет! Несколько дней назад на Github состоялся релиз фреймворка tramvai - https://github.com/TinkoffCreditSystems/tramvai tramvai - это фреймворк для создания SSR приложений на React, внутренняя разработка Тинькофф, и последние полтора года я работаю в…»
Привет!
Несколько дней назад прочитал статью от Shawn Wang https://twitter.com/swyx - "Why do Webdevs keep trying to kill REST?" - https://www.swyx.io/client-server-battle/
В этой статье представлен интересный взгляд на дебаты REST против GraphQL - Swyx рассказывает, что на самом деле эти споры про
Больше всего меня впечатлил широкий кругозор автора - Swyx приводит в примерах множество технологий, о которых я даже не слышал, но которые имеют прямое отношение к фронтенд разработке.
Поэтому, после свобдного перевода статьи, хочу сделать небольшие обзоры этих новых для меня технологий.
Smart Client - это подход, при котором сначала обновляется состояние на клиенте, и затем отправляется на сервер.
Рассмотрим на примерах:
- Кастомный smart client - решение на основе Redux, или сторов Svelte, где мы самостоятельно управляем координацией данных на сервере и клиенте
- Специализированная библиотека для управления состоянием и запросами - React Query, Apollo Client, RxDB, GunDB, WatermelonDB и Absurd-SQL
- Фреймворк, который абстрагирует координацию данных - Next.js или Blitz.js (тут я не совсем понял автора, т.к. Next.js например предоставляет интеграцию с библиотекой
- SDK к облачной платформе - Google's Firebase или AWS Amplify / AppSync, из коробки предоставляют интеграции с такими бэкенд ресурсами как авторизация, база данных и другие хранилища
Smart Server - обратный подход, когда обновления состояния сначала отправляются на сервер, который в свою очередь отправляет обновленный view на клиент (HTML чанки, сериализованные React компоненты или XML)
Пример актуальных технологий:
- Phoenix Liveview
- Rails Hotwire
- React Server Components
- ASP.NET Web Forms
Smart Server - это не новый подход, а эволюция традиционного серверного рендеринга.
Laravel, Django, Wordpress и ряд других фреймворков отправляют на клиент отрендеренные HTML шаблоны, а клиент добавляет интерактивность, и отправляет запросы на REST эндпоинты сервера, такое классическое разделение на фронтенд и бэкенд.
Из моего опыта, я успел поучаствовать в двух таких проектах, где
Почему же мы уходим от старой клиент-серверной парадигмы, и какой подход лучше?
Несколько дней назад прочитал статью от Shawn Wang https://twitter.com/swyx - "Why do Webdevs keep trying to kill REST?" - https://www.swyx.io/client-server-battle/
В этой статье представлен интересный взгляд на дебаты REST против GraphQL - Swyx рассказывает, что на самом деле эти споры про
Smart Servers против Smart Clients (не стал переводить в лоб, возможно тут подойдут термины тонкий / толстые клиент или сервер)Больше всего меня впечатлил широкий кругозор автора - Swyx приводит в примерах множество технологий, о которых я даже не слышал, но которые имеют прямое отношение к фронтенд разработке.
Поэтому, после свобдного перевода статьи, хочу сделать небольшие обзоры этих новых для меня технологий.
Smart Client - это подход, при котором сначала обновляется состояние на клиенте, и затем отправляется на сервер.
Рассмотрим на примерах:
- Кастомный smart client - решение на основе Redux, или сторов Svelte, где мы самостоятельно управляем координацией данных на сервере и клиенте
- Специализированная библиотека для управления состоянием и запросами - React Query, Apollo Client, RxDB, GunDB, WatermelonDB и Absurd-SQL
- Фреймворк, который абстрагирует координацию данных - Next.js или Blitz.js (тут я не совсем понял автора, т.к. Next.js например предоставляет интеграцию с библиотекой
swr, аналогом React Query, но в чистом виде ничего специфичного не дает)- SDK к облачной платформе - Google's Firebase или AWS Amplify / AppSync, из коробки предоставляют интеграции с такими бэкенд ресурсами как авторизация, база данных и другие хранилища
Smart Server - обратный подход, когда обновления состояния сначала отправляются на сервер, который в свою очередь отправляет обновленный view на клиент (HTML чанки, сериализованные React компоненты или XML)
Пример актуальных технологий:
- Phoenix Liveview
- Rails Hotwire
- React Server Components
- ASP.NET Web Forms
Smart Server - это не новый подход, а эволюция традиционного серверного рендеринга.
Laravel, Django, Wordpress и ряд других фреймворков отправляют на клиент отрендеренные HTML шаблоны, а клиент добавляет интерактивность, и отправляет запросы на REST эндпоинты сервера, такое классическое разделение на фронтенд и бэкенд.
Из моего опыта, я успел поучаствовать в двух таких проектах, где
php фреймворк отвечал за рендеринг HTML шаблонов.Почему же мы уходим от старой клиент-серверной парадигмы, и какой подход лучше?
X (formerly Twitter)
swyx 🇸🇬 (@swyx) on X
achieve ambition with intentionality, intensity, & integrity
affils:
- @dxtipshq
- @cognition
- @sveltesociety
- @aidotengineer
- @latentspacepod + @smol_ai
affils:
- @dxtipshq
- @cognition
- @sveltesociety
- @aidotengineer
- @latentspacepod + @smol_ai
👍1
Про User Experience
Smart Clients и Smart Servers имеют ряд плюсов и минусов, и нельзя однозначно выбрать лучший подход, все зависит от того, какое приложение мы разрабатываем.
Smart Clients позволяют делать offline-first приложения с оптимистичными обновлениями, т.е. могут работать без интернета и мгновенно реагировать на любые действия:
- это улучшает интуитивное впечатление пользователя о скорости работы приложения
- но раздувает JS бандлы на клиент, SDK Firebase добавляет 1mb кода в бандл, SDK Amplify около 230kb
Smart Servers напрямую уменьшают количество JS кода, т.к. делают большую часть работы на сервере. Например, при интеграции React Server Components в Facebook, рассказали про уменьшении JS кода на 29% - https://twitter.com/swyx/status/1341151070743982080
- это улучшает скорость первой загрузки сайта, и уменьшает количество JS, загружаемого на клиенте
- но нагрузка по рендерингу каждого кусочка приложения ложится на ваши серверы, и они тратят значительно больше ресурсов
Про Developer Experience
Платформенные SDK - такие платформы как Firebase и AWS Amplify могут предоставлять лучший DX на фронте, т.к. имеют полное понимание вашего бэкенда
Уменьшение бойлерплейта - вместо отдельного написания бэкенд обработчиков, и клиентских запросов к API, мы можем генерировать кастомный API клиент
- Smart Servers радикально уменьшают бойлерплейт, т.к. самостоятельно реализуют механизм синхронизации. В пример приводится LiveView - https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html
Работа offline - платформенные SDK, независимые библиотеки RxDB или Redux Offline, позволяют иметь локальную копию данных, и легко разрешать конфликты обновления этих данных
Уменьшение бойлерплейта для оптимистичных обновлений - имея локальную копию данных, мы просто записываем в нее изменение, и ждем синхронизации с сервером.
В итоге, оба подхода дают значительные улучшения DX.
Про протоколы
Хороший протокол улучшает UX и DX, например:
Типобезопасность - GraphQL проверяет типы в рантайме,
Пропускная способность сети - уменьшение передаваемых данных:
- GraphQL решает проблему запроса избыточных данных (кажется проблема не так важна, если вы не IT гигант)
-
- React Server Components отправляют сериализуемые компоненты
Real-time - возможности совместной работы над одним приложением:
- Для этого отлично подходит UDP, WebRTC и WebSockets (отмечу от себя, что WebSockets работает поверх TCP, и это можно отнести к недостаткам протокола, а WebRTC похоже позволяет выбрать транспорт самостоятельно)
- Также подходящими выглядят такие инструменты как
- UDP в целом похож на отличный фундамент для новых инновационных протоколов (HTTP/3 работает поверх UDP)
В конце статьи автор говорит о том, что заголовок немного кликбейт, и конечно REST протокол отлично подходит для большинства приложений.
Smart Clients и Smart Servers имеют ряд плюсов и минусов, и нельзя однозначно выбрать лучший подход, все зависит от того, какое приложение мы разрабатываем.
Smart Clients позволяют делать offline-first приложения с оптимистичными обновлениями, т.е. могут работать без интернета и мгновенно реагировать на любые действия:
- это улучшает интуитивное впечатление пользователя о скорости работы приложения
- но раздувает JS бандлы на клиент, SDK Firebase добавляет 1mb кода в бандл, SDK Amplify около 230kb
Smart Servers напрямую уменьшают количество JS кода, т.к. делают большую часть работы на сервере. Например, при интеграции React Server Components в Facebook, рассказали про уменьшении JS кода на 29% - https://twitter.com/swyx/status/1341151070743982080
- это улучшает скорость первой загрузки сайта, и уменьшает количество JS, загружаемого на клиенте
- но нагрузка по рендерингу каждого кусочка приложения ложится на ваши серверы, и они тратят значительно больше ресурсов
Про Developer Experience
Платформенные SDK - такие платформы как Firebase и AWS Amplify могут предоставлять лучший DX на фронте, т.к. имеют полное понимание вашего бэкенда
Уменьшение бойлерплейта - вместо отдельного написания бэкенд обработчиков, и клиентских запросов к API, мы можем генерировать кастомный API клиент
- Smart Servers радикально уменьшают бойлерплейт, т.к. самостоятельно реализуют механизм синхронизации. В пример приводится LiveView - https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html
Работа offline - платформенные SDK, независимые библиотеки RxDB или Redux Offline, позволяют иметь локальную копию данных, и легко разрешать конфликты обновления этих данных
Уменьшение бойлерплейта для оптимистичных обновлений - имея локальную копию данных, мы просто записываем в нее изменение, и ждем синхронизации с сервером.
В итоге, оба подхода дают значительные улучшения DX.
Про протоколы
Хороший протокол улучшает UX и DX, например:
Типобезопасность - GraphQL проверяет типы в рантайме,
trpc на этапе компиляцииПропускная способность сети - уменьшение передаваемых данных:
- GraphQL решает проблему запроса избыточных данных (кажется проблема не так важна, если вы не IT гигант)
-
Hotwire буквально передает HTML по проводам- React Server Components отправляют сериализуемые компоненты
Real-time - возможности совместной работы над одним приложением:
- Для этого отлично подходит UDP, WebRTC и WebSockets (отмечу от себя, что WebSockets работает поверх TCP, и это можно отнести к недостаткам протокола, а WebRTC похоже позволяет выбрать транспорт самостоятельно)
- Также подходящими выглядят такие инструменты как
Replicache и Croquet- UDP в целом похож на отличный фундамент для новых инновационных протоколов (HTTP/3 работает поверх UDP)
В конце статьи автор говорит о том, что заголовок немного кликбейт, и конечно REST протокол отлично подходит для большинства приложений.
Мой опыт разработки клиентских приложений поверх традиционной серверной модели достаточно болезненных - в HTML шаблонах (наверное на любом шаблонизаторе) отсутствует типизация, а данные, передаваемые в шаблоны, как правило глобальные, и доступны каждому вложенному темплейту, отладка шаблонов это отдельная сложность.
Наш клиентский код должен подключаться к разметке, которую отдал сервер, и добавлять ей интерактивность - это значит, что даже разделив JS код с логикой компонентов, нам придется грузить все эти компоненты на каждой странице, либо вручную реализовывать lazy loading (эту работу мог за нас сделать бандлер и code splitting)
Также, скорее всего нам придется поддерживать одинаковый набор компонентов на сервере (в шаблонах) и на клиенте (на выбранном фреймворке), а переиспользовать мы сможем максимум стили.
По этой причине, возможность писать универсальные для сервера и клиента компоненты на одном языке и фреймворке для меня выглядит даром богов, и единственным правильным способом делать фронтенд (конечно исключая кейсы, когда нам не нужен серверный рендеринг). Это дает нам гибкость, и переиспользуемый код.
Но, если посмотреть трезвым взглядом, универсальные SSR приложения на React имеют свои недостатки:
- Мы должны писать наш сервер на NodeJS (знаю, что есть решения для запуска JS кода в других языках, и есть
С другой стороны, есть такие решения как
- Это медленно.
Рендеринг и запросы происходят на сервере, что просаживает Time To First Byte.
Из-за ожидания загрузки кода и гидрации на клиенте, просаживается Time To Interactive.
Хорошая статья про виды рендеринга в вэбе от Эдди Османи - https://developers.google.com/web/updates/2019/02/rendering-on-the-web
- Много JavaScript кода (медленно v2).
В первую очередь - это код выбранного фреймворка.
Плюс, даже сложное веб приложение может иметь не так много интерактивных компонентов на странице.
Точечное добавление обработчиков событий и логики обновления DOM вместо отдельного кода на каждый статичный HTML тег позволит сильно уменьшить количество кода на странице.
Определенно, React Server Components частично помогает решить эту проблему, т.к. исключает со стороны клиента лишний код для фетчинга данных.
Наш клиентский код должен подключаться к разметке, которую отдал сервер, и добавлять ей интерактивность - это значит, что даже разделив JS код с логикой компонентов, нам придется грузить все эти компоненты на каждой странице, либо вручную реализовывать lazy loading (эту работу мог за нас сделать бандлер и code splitting)
Также, скорее всего нам придется поддерживать одинаковый набор компонентов на сервере (в шаблонах) и на клиенте (на выбранном фреймворке), а переиспользовать мы сможем максимум стили.
По этой причине, возможность писать универсальные для сервера и клиента компоненты на одном языке и фреймворке для меня выглядит даром богов, и единственным правильным способом делать фронтенд (конечно исключая кейсы, когда нам не нужен серверный рендеринг). Это дает нам гибкость, и переиспользуемый код.
Но, если посмотреть трезвым взглядом, универсальные SSR приложения на React имеют свои недостатки:
- Мы должны писать наш сервер на NodeJS (знаю, что есть решения для запуска JS кода в других языках, и есть
Deno, но кажется все эти решения не оптимальны как минимум с точки зрения производительности) - и это требует экспертизу, которой может не быть у фронтенд разработчиков на текущем проекте.С другой стороны, есть такие решения как
NextJS и tramvai, которые возьмут на себя большую часть сложностей на серверной стороне.- Это медленно.
Рендеринг и запросы происходят на сервере, что просаживает Time To First Byte.
Из-за ожидания загрузки кода и гидрации на клиенте, просаживается Time To Interactive.
Хорошая статья про виды рендеринга в вэбе от Эдди Османи - https://developers.google.com/web/updates/2019/02/rendering-on-the-web
- Много JavaScript кода (медленно v2).
В первую очередь - это код выбранного фреймворка.
Плюс, даже сложное веб приложение может иметь не так много интерактивных компонентов на странице.
Точечное добавление обработчиков событий и логики обновления DOM вместо отдельного кода на каждый статичный HTML тег позволит сильно уменьшить количество кода на странице.
Определенно, React Server Components частично помогает решить эту проблему, т.к. исключает со стороны клиента лишний код для фетчинга данных.
web.dev
Rendering on the Web | Articles | web.dev
Recommendations for implementing logic and rendering in apps.
Для меня очень интересным оказался опыт разработки
Github разработчики ушли от jQuery, и реализуют интерфейсы с помощью нескольких небольших утилит для делегирования событий, и создания качественных web-components - https://github.blog/2018-09-06-removing-jquery-from-github-frontend/ и https://github.blog/2021-05-04-how-we-use-web-components-at-github/
На бэкенде используется Ruby on Rails, и разработчики "прокачали" работу с шаблонами в Rails, добавив инкапсуляцию в шаблоны с помощью ViewComponent - https://github.blog/2020-12-15-encapsulating-ruby-on-rails-views/
Кстати, посмотреть исходный код фронта Github вы можете с легкостью, т.к. sourcemaps в открытом доступе, и достаточно открыть инструменты разработчика, вкладку Sources, и открыть там например файл
Подход Github выглядит шагом назад с точки зрения DX, но кажется ViewComponent сильно упростили фронтам работу с шаблонами на сервере.
С точки зрения UX, приведу в пример сравнение нашей главной странички https://tinkoff.ru и страничку репозитория tramvai - https://github.com/TinkoffCreditSystems/tramvai
Github, где, если я правильно понял концепцию, полноценно используется подход Smart Server.Github разработчики ушли от jQuery, и реализуют интерфейсы с помощью нескольких небольших утилит для делегирования событий, и создания качественных web-components - https://github.blog/2018-09-06-removing-jquery-from-github-frontend/ и https://github.blog/2021-05-04-how-we-use-web-components-at-github/
На бэкенде используется Ruby on Rails, и разработчики "прокачали" работу с шаблонами в Rails, добавив инкапсуляцию в шаблоны с помощью ViewComponent - https://github.blog/2020-12-15-encapsulating-ruby-on-rails-views/
Кстати, посмотреть исходный код фронта Github вы можете с легкостью, т.к. sourcemaps в открытом доступе, и достаточно открыть инструменты разработчика, вкладку Sources, и открыть там например файл
https://github.githubassets.com/assets/app/assets/modules/behaviors.tsПодход Github выглядит шагом назад с точки зрения DX, но кажется ViewComponent сильно упростили фронтам работу с шаблонами на сервере.
С точки зрения UX, приведу в пример сравнение нашей главной странички https://tinkoff.ru и страничку репозитория tramvai - https://github.com/TinkoffCreditSystems/tramvai
The GitHub Blog
Removing jQuery from GitHub.com frontend
We have recently completed a milestone where we were able to drop jQuery as a dependency of the frontend code for GitHub.com. This marks the end of a gradual, years-long…
На tinkoff.ru загружается около 250kb gzip вендор кода и кода приложения, а на github.com - около 200kb.
При этом, страница репозитория выглядит значительно более сложной и интерактивной.
tinkoff.ru разработан на фреймворке
Мы постоянно работаем над улучшением производительности, но у нас остаются достаточно хардкорные оптимизации, которые могут дать заметный профит - lazy hydration каждого блока на странице, отключение hydration для блоков, которые статические по своей природе, или например tree-shaking DI провайдеров во время компиляции приложения (пример такого подхода у Angular - https://coryrylan.com/blog/tree-shakeable-providers-and-services-in-angular)
Сравнение не очень корректное, т.к. я сравниваю разработку с фреймворком, и без фреймворка - но все же, подход Github впечатляет, и исходный код выглядит хорошо.
Есть ощущение, что фронты Github научились готовить традиционную серверную модель работы веб приложения, делают это без боли, и дают хорошие результаты.
Возможно ViewComponent - https://github.com/github/view_component - это не совсем Smart Server, а более традиционный подход, но "на стероидах", позволяющий делать более качественные серверные компоненты.
При этом, страница репозитория выглядит значительно более сложной и интерактивной.
tinkoff.ru разработан на фреймворке
tramvai, и каждый блок на странице является микрофронтендом на React.Мы постоянно работаем над улучшением производительности, но у нас остаются достаточно хардкорные оптимизации, которые могут дать заметный профит - lazy hydration каждого блока на странице, отключение hydration для блоков, которые статические по своей природе, или например tree-shaking DI провайдеров во время компиляции приложения (пример такого подхода у Angular - https://coryrylan.com/blog/tree-shakeable-providers-and-services-in-angular)
Сравнение не очень корректное, т.к. я сравниваю разработку с фреймворком, и без фреймворка - но все же, подход Github впечатляет, и исходный код выглядит хорошо.
Есть ощущение, что фронты Github научились готовить традиционную серверную модель работы веб приложения, делают это без боли, и дают хорошие результаты.
Возможно ViewComponent - https://github.com/github/view_component - это не совсем Smart Server, а более традиционный подход, но "на стероидах", позволяющий делать более качественные серверные компоненты.
Coryrylan
Tree Shakeable Providers and Services in Angular - Angular 17 | 16
Learn how to leverage tree shakable providers in Angular for better application performance.
Swyx говорил еще про несколько интересных технологий:
Phoenix LiveView
Сложно уместить описание в несколько строк, но даже так выглядит мощно!
Rails Hotwire
Подробное видео с разбором работы инструмента можно посмотреть тут - https://hotwired.dev/
Еще одно из отличий Hotwire от LiveView, и кажется преимущество - все частичные апдейты страницы делаются по HTTP протоколу, WebSockets можно использовать для страниц, где нужна совместная работа нескольких пользователей. LiveView же использует WebSockets для всех апдейтов.
Также попалась статья, где еще больше похожих на LiveView и HotWire инструментов - https://dev.to/rajasegar/html-over-the-wire-is-the-future-of-web-development-542c
Для меня это совершенно незнакомый мир, и очень интересно видеть, как по разному может выглядеть фронтенд разработка!
Phoenix LiveView
Phoenix - https://github.com/phoenixframework/phoenix - это серверный фреймворк на ElixirLiveView - https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html - это расширение фреймворка, реализующую real-time синхронизацию между сервером и клиентом, возможность разделения серверного view кода на компоненты, реализацию реакцие на взаимодействие пользователей на сервере, и позволяющее отказаться от SPA фреймворка на клиенте и в целом писать минимум кода.Сложно уместить описание в несколько строк, но даже так выглядит мощно!
Rails Hotwire
Hotwire - https://github.com/hotwired/hotwire-rails - расширение для Ruby фреймворка Rails, дает схожие возможности с Phoenix LiveView, одно из отличий - это использование HTML в качестве типа данных общения с сервером, для любых апдейтов DOM.Подробное видео с разбором работы инструмента можно посмотреть тут - https://hotwired.dev/
Еще одно из отличий Hotwire от LiveView, и кажется преимущество - все частичные апдейты страницы делаются по HTTP протоколу, WebSockets можно использовать для страниц, где нужна совместная работа нескольких пользователей. LiveView же использует WebSockets для всех апдейтов.
Также попалась статья, где еще больше похожих на LiveView и HotWire инструментов - https://dev.to/rajasegar/html-over-the-wire-is-the-future-of-web-development-542c
Для меня это совершенно незнакомый мир, и очень интересно видеть, как по разному может выглядеть фронтенд разработка!
GitHub
GitHub - phoenixframework/phoenix: Peace of mind from prototype to production
Peace of mind from prototype to production. Contribute to phoenixframework/phoenix development by creating an account on GitHub.
Актуальный и классный доклад от Рича Харриса про подходы к созданию веб-приложений - https://youtu.be/860d8usGC0o
Во многом перекликается с статьей про Smart Servers и Smart Clients от Swyx, на которую я недавно писал обзор - https://news.1rj.ru/str/super_oleg_dev/27.
Автор подробно рассматривает преимущества и проблемы традиционного подхода к рендерингу страниц на сервере (MPA приложения) и SPA приложений.
Не забыты и Phoenix LiveView вместе Rails Hotwire, как эволюция MPA подхода, но не решающие всех проблем (работа offline, optimistic updates)
Очень метко про github.com - показал ряд рассинхронизаций интерфейса, бороться с которыми обречено приложение, обновляющее UI по частям, без общего стейта и механизма для рендеринга.
Почти все из проблем из преимуществ уже были озвучены в предыдущих постах - https://news.1rj.ru/str/super_oleg_dev/29, поэтому хочу рассказать про то, что Рич предлагает для решения проблем этих подходов.
Автор предлагает новый термин, по аналогии с transitional design - transitional apps, приложения, которые берут лучшее из MPA и SPA миров.
Далее приводит несколько примеров как библиотеки и фреймворки решают недостатки совмещения этих подходов:
- React Server Components (кажется пора сделать отдельный обзор технологии, к сожалению не делал заметок, когда первый раз читал RFC https://github.com/reactjs/rfcs/pull/188)
- marco с их механизмом частичной гидрации интерактивных частей приложения https://github.com/marko-js/marko
- qwic с агрессивным lazy loading всего кода страницы https://github.com/BuilderIO/qwik
- astro с архитектурой islands, также предназначенной для загрузки только необходимого кода https://github.com/snowpackjs/astro
- svelte, который снижает стоимость фреймворка, компилируя только необходимый код, и позволяющий не использовать гидрацию https://github.com/sveltejs/svelte
В конце Рич рассказывает про SvelteKit https://github.com/sveltejs/kit, и как этот инструмент предоставляет лучшее из обоих миров, на примере небольшого демо-приложения.
Если обобщить, SvelteKit из коробки позволяет приложению:
- работать без JS (например для перехода по ссылкам и отправки форм)
- рендерить на этапе сборки статические страницы, с опциональным добавлением JS для интерактивности
- простой деплой всего приложения на комбинацию edge functions и cdn
Классный и короткий доклад, частично анонс будущего доклада Рича Харриса, рекомендую к просмотру!
Отдельно пометил себе попробовать SvelteKit, интересно насколько всё-таки хорошо решается проблема лишнего кода фреймворков.
DX по определению будет отличный благодаря Vite (невероятно крут из коробки на новых приложениях, но есть сомнения, будет ли эффективна разработка приложений где на страницу загружаются сотни и сотни js модулей, даже по http/2)
Также, в видео есть пример нескольких багов, связанных с SPA маршрутизацией - кажется что это проблема конкретной реализации, а не SPA фреймворков.
Во многом перекликается с статьей про Smart Servers и Smart Clients от Swyx, на которую я недавно писал обзор - https://news.1rj.ru/str/super_oleg_dev/27.
Автор подробно рассматривает преимущества и проблемы традиционного подхода к рендерингу страниц на сервере (MPA приложения) и SPA приложений.
Не забыты и Phoenix LiveView вместе Rails Hotwire, как эволюция MPA подхода, но не решающие всех проблем (работа offline, optimistic updates)
Очень метко про github.com - показал ряд рассинхронизаций интерфейса, бороться с которыми обречено приложение, обновляющее UI по частям, без общего стейта и механизма для рендеринга.
Почти все из проблем из преимуществ уже были озвучены в предыдущих постах - https://news.1rj.ru/str/super_oleg_dev/29, поэтому хочу рассказать про то, что Рич предлагает для решения проблем этих подходов.
Автор предлагает новый термин, по аналогии с transitional design - transitional apps, приложения, которые берут лучшее из MPA и SPA миров.
Далее приводит несколько примеров как библиотеки и фреймворки решают недостатки совмещения этих подходов:
- React Server Components (кажется пора сделать отдельный обзор технологии, к сожалению не делал заметок, когда первый раз читал RFC https://github.com/reactjs/rfcs/pull/188)
- marco с их механизмом частичной гидрации интерактивных частей приложения https://github.com/marko-js/marko
- qwic с агрессивным lazy loading всего кода страницы https://github.com/BuilderIO/qwik
- astro с архитектурой islands, также предназначенной для загрузки только необходимого кода https://github.com/snowpackjs/astro
- svelte, который снижает стоимость фреймворка, компилируя только необходимый код, и позволяющий не использовать гидрацию https://github.com/sveltejs/svelte
В конце Рич рассказывает про SvelteKit https://github.com/sveltejs/kit, и как этот инструмент предоставляет лучшее из обоих миров, на примере небольшого демо-приложения.
Если обобщить, SvelteKit из коробки позволяет приложению:
- работать без JS (например для перехода по ссылкам и отправки форм)
- рендерить на этапе сборки статические страницы, с опциональным добавлением JS для интерактивности
- простой деплой всего приложения на комбинацию edge functions и cdn
Классный и короткий доклад, частично анонс будущего доклада Рича Харриса, рекомендую к просмотру!
Отдельно пометил себе попробовать SvelteKit, интересно насколько всё-таки хорошо решается проблема лишнего кода фреймворков.
DX по определению будет отличный благодаря Vite (невероятно крут из коробки на новых приложениях, но есть сомнения, будет ли эффективна разработка приложений где на страницу загружаются сотни и сотни js модулей, даже по http/2)
Также, в видео есть пример нескольких багов, связанных с SPA маршрутизацией - кажется что это проблема конкретной реализации, а не SPA фреймворков.
YouTube
Have Single-Page Apps Ruined the Web? | Transitional Apps with Rich Harris, NYTimes
The backlash to modern front end development is gaining steam, with good reason: single-page apps have ruined the web. Can we rescue it without going backwards? In this talk, Rich Harris presents a way to do just that. Rich Harris is a graphics editor at…
Привет!
Попалась небольшая статья про загрузку данных на уровне компонентов в NextJS приложениях - https://medium.com/@A__G__B/component-level-data-fetching-in-next-js-with-srr-8d35cdc5849e
Загрузка данных на уровне компонентов обеспечивается с помощью либы https://github.com/kmoskwiak/useSSE
Оказалось, что use-sse использует подход double render (если я не ошибаюсь, его использует Appolo).
Первый рендеринг приложения на сервере позволяет собрать промисы с загрузкой данных для каждого компонента, второй рендеринг выполняется после резолва этих промисов.
Меня всегда сильно смущал такой подход - дело в том, что ReactDOMServer.renderToString и даже ReactDOMServer.renderToNodeStream работают синхронно, и соответственно блокируют event loop.
На гитхабе мне попадались заброшенные попытки форкнуть ReactDOMServer и сделать рендеринг асинхронным, выполнять его по кусочкам, как раз для предотвращения долгой блокировки event loop.
Почему это важно?
React работает быстро на сервере, но для большого дерева компонентов, renderToString может занимать условно 100ms (на примере некоторых наших приложений от 30 до 300 при больших нагрузках).
Скорее всего, это будет самая длинная синхронная операция на вашем сервере.
Фактически, эта операция - самый главный ограничитель вашего RPS на одну ноду с SSR приложением.
Если я правильно понимаю как работает нода, когда приложение получает одновременно десять запросов, с синхронными операциями по 100ms, одинадцатый запрос получит ответ минимум через секунду, т.е. они буквально станут в очередь (это все без учёта прочих асинхронных действий на сервере).
Каждый новый запрос будет ухудшать ситуацию, увеличивать время ответа следующего запроса, будет заметно больше лаг event loop.
С такими метриками, при SSR с React можно рассчитывать что одна нода потянет 10 RPS, что достаточно не серьезные нагрузки с точки зрения high load, но пока что это наша фронтовая реальность.
С учётом этих факторов, double rendering кажется чем-то невероятно дорогим.
И в будущем Server Components будут решать проблему загрузки данных на уровне компонентов.
Я очень рассчитываю на React 18 и новую архитектуру работы на сервере.
Новые механизмы для рендеринга на сервере, вместе с Suspense, больше не будут рендерить приложение в один синхронный проход, эта задача будет разделена на отдельные асинхронные задачи (одна задача на один юнит - реакт компонент), аналогично concurrent rendering на клиенте.
Если node.js вместо десятка синхронных задач по 100ms будет получать тысячу задачек по 1ms, это в разы увеличит количество запросов, которые нода может обрабатывать одновременно без существенного влияния перформанс, т.е. новые запросы будут гораздо меньше влиять на время ответа для последующих запросов.
Например, все запросы, которые будут возвращать ошибки 500 или 404, или редиректы, не будут ждать синхронную очередь, а смогут вклиниться между остальными запросами.
Из минусов при асинхронном рендеринге - увеличение RPS будет примерно одинаково по чуть-чуть замедлять ответы на все обрабатываемые запросы, в случае с синхронными рендерингом первый запрос в очереди всегда будет обработан максимально быстро.
И возможно в новом прекрасном мире SSR легче будет делать high load :)
Отдельно хочу вспомнить про кэширование.
Конечно, самое эффективное ускорение рендеринга на сервере - это его отсутствие, и в ряде случаев можно кэшировать результаты рендеринга, или даже использовать SSG.
Но в приложениях с большим количеством персонализации, скорее всего кэшировать будет просто нечего, т.к. каждый пользователь будет получать уникальный HTML.
В tramvai приложениях на tinkoff.ru мы активно кэшируем на сервере только ответы на запросы, которые не требуют персонализации.
В конце хочу порекламировать свой обзор дискуссий reactwg/react-18, в том числе там есть разбор новой архитектуры на сервере - https://superoleg39.notion.site/reactwg-react-18-3914d12cc91e430b974495bffea86472
Попалась небольшая статья про загрузку данных на уровне компонентов в NextJS приложениях - https://medium.com/@A__G__B/component-level-data-fetching-in-next-js-with-srr-8d35cdc5849e
Загрузка данных на уровне компонентов обеспечивается с помощью либы https://github.com/kmoskwiak/useSSE
Оказалось, что use-sse использует подход double render (если я не ошибаюсь, его использует Appolo).
Первый рендеринг приложения на сервере позволяет собрать промисы с загрузкой данных для каждого компонента, второй рендеринг выполняется после резолва этих промисов.
Меня всегда сильно смущал такой подход - дело в том, что ReactDOMServer.renderToString и даже ReactDOMServer.renderToNodeStream работают синхронно, и соответственно блокируют event loop.
На гитхабе мне попадались заброшенные попытки форкнуть ReactDOMServer и сделать рендеринг асинхронным, выполнять его по кусочкам, как раз для предотвращения долгой блокировки event loop.
Почему это важно?
React работает быстро на сервере, но для большого дерева компонентов, renderToString может занимать условно 100ms (на примере некоторых наших приложений от 30 до 300 при больших нагрузках).
Скорее всего, это будет самая длинная синхронная операция на вашем сервере.
Фактически, эта операция - самый главный ограничитель вашего RPS на одну ноду с SSR приложением.
Если я правильно понимаю как работает нода, когда приложение получает одновременно десять запросов, с синхронными операциями по 100ms, одинадцатый запрос получит ответ минимум через секунду, т.е. они буквально станут в очередь (это все без учёта прочих асинхронных действий на сервере).
Каждый новый запрос будет ухудшать ситуацию, увеличивать время ответа следующего запроса, будет заметно больше лаг event loop.
С такими метриками, при SSR с React можно рассчитывать что одна нода потянет 10 RPS, что достаточно не серьезные нагрузки с точки зрения high load, но пока что это наша фронтовая реальность.
С учётом этих факторов, double rendering кажется чем-то невероятно дорогим.
И в будущем Server Components будут решать проблему загрузки данных на уровне компонентов.
Я очень рассчитываю на React 18 и новую архитектуру работы на сервере.
Новые механизмы для рендеринга на сервере, вместе с Suspense, больше не будут рендерить приложение в один синхронный проход, эта задача будет разделена на отдельные асинхронные задачи (одна задача на один юнит - реакт компонент), аналогично concurrent rendering на клиенте.
Если node.js вместо десятка синхронных задач по 100ms будет получать тысячу задачек по 1ms, это в разы увеличит количество запросов, которые нода может обрабатывать одновременно без существенного влияния перформанс, т.е. новые запросы будут гораздо меньше влиять на время ответа для последующих запросов.
Например, все запросы, которые будут возвращать ошибки 500 или 404, или редиректы, не будут ждать синхронную очередь, а смогут вклиниться между остальными запросами.
Из минусов при асинхронном рендеринге - увеличение RPS будет примерно одинаково по чуть-чуть замедлять ответы на все обрабатываемые запросы, в случае с синхронными рендерингом первый запрос в очереди всегда будет обработан максимально быстро.
И возможно в новом прекрасном мире SSR легче будет делать high load :)
Отдельно хочу вспомнить про кэширование.
Конечно, самое эффективное ускорение рендеринга на сервере - это его отсутствие, и в ряде случаев можно кэшировать результаты рендеринга, или даже использовать SSG.
Но в приложениях с большим количеством персонализации, скорее всего кэшировать будет просто нечего, т.к. каждый пользователь будет получать уникальный HTML.
В tramvai приложениях на tinkoff.ru мы активно кэшируем на сервере только ответы на запросы, которые не требуют персонализации.
В конце хочу порекламировать свой обзор дискуссий reactwg/react-18, в том числе там есть разбор новой архитектуры на сервере - https://superoleg39.notion.site/reactwg-react-18-3914d12cc91e430b974495bffea86472
Medium
Component-level data fetching in Next.js (with SSR)
Shout-out to Zack Jackson for creating the design and solution outlined below.
Привет!
Несколько часов назад угнали пакет
Библиотека с 8 лямов скачиваний в неделю, активно используем в tramvai.
Удалить пакеты так просто автор кажется не сможет из-за политики npm после leftpad, поэтому рекомендую проверить зависимости в своих проектах, и по необходимости зафиксировать версию
Информации пока немного, завел issue - https://github.com/faisalman/ua-parser-js/issues/536
Надеюсь, что всё-таки скоро откатят пакет, но предупредить будет не лишним)
Несколько часов назад угнали пакет
ua-parser-js, и опубликовали три вредоносные версии, возможно с криптомайнером.Библиотека с 8 лямов скачиваний в неделю, активно используем в tramvai.
Удалить пакеты так просто автор кажется не сможет из-за политики npm после leftpad, поэтому рекомендую проверить зависимости в своих проектах, и по необходимости зафиксировать версию
ua-parser-js на 0.7.28Информации пока немного, завел issue - https://github.com/faisalman/ua-parser-js/issues/536
Надеюсь, что всё-таки скоро откатят пакет, но предупредить будет не лишним)
GitHub
Security issue: compromised npm packages of ua-parser-js (0.7.29, 0.8.0, 1.0.0) - Questions about deprecated npm package ua-parser…
Hi! See a warning at npm - https://www.npmjs.com/package/ua-parser-js - This package has been hijacked. Please revert to 0.7.28 First question - Can we use range ^0.7.28, or it is not safe? Second ...
Мейнтейнер ua-parser-js опубликовал версии пакета без уязвимостей, перекрывающие проблемные по semver - 0.7.30, 0.8.1 и 1.0.1
Это закрывает основные проблемные кейсы - свежая установка диапазонов версий вида ^0.7.28 и latest версии.
Конечно, до отката пакетов в npm остаётся проблема у тех, кто уже успел скачать и закэшировать версии.
Дополнительно, в нашем приватном регистре пакетов мы удалили проблемные версии и запретили их проксирование из публичного npm
Это закрывает основные проблемные кейсы - свежая установка диапазонов версий вида ^0.7.28 и latest версии.
Конечно, до отката пакетов в npm остаётся проблема у тех, кто уже успел скачать и закэшировать версии.
Дополнительно, в нашем приватном регистре пакетов мы удалили проблемные версии и запретили их проксирование из публичного npm
Подводя итоги истории с ua-parser-js:
- npm откатили пакет примерно через 6 часов после публикации версии с уязвимостью, это достаточно быстро, но например я успел скачать эту уязвимость около пяти раз (хорошо это было в контейнере)
- мейнтейнер хорошо сделал, что пометил проблемные версии как deprecated, но самым лучшим решением было бы сразу опубликовать нормальные версии, перекрыв по semver проблемные, в итоге это заняло около 4-х часов
- фиксирование версии пакета в зависимостях библиотек и приложений кажется не имеет большого смысла, даже если у вас быстрый релизный цикл, пока все кто тянут ua-parser-js обновятся, пройдет много времени
- возможность быстро настроить фильтрацию этих пакетов в вашем приватном регистре более удачный вариант, чем фиксация версий
- мейнтейнеру посоветовали включить 2FA
- ряд советов в issue как проверить что вы установили проблемный пакет, как минимум стоит проверить что не запущен процесс
- npm откатили пакет примерно через 6 часов после публикации версии с уязвимостью, это достаточно быстро, но например я успел скачать эту уязвимость около пяти раз (хорошо это было в контейнере)
- мейнтейнер хорошо сделал, что пометил проблемные версии как deprecated, но самым лучшим решением было бы сразу опубликовать нормальные версии, перекрыв по semver проблемные, в итоге это заняло около 4-х часов
- фиксирование версии пакета в зависимостях библиотек и приложений кажется не имеет большого смысла, даже если у вас быстрый релизный цикл, пока все кто тянут ua-parser-js обновятся, пройдет много времени
- возможность быстро настроить фильтрацию этих пакетов в вашем приватном регистре более удачный вариант, чем фиксация версий
- мейнтейнеру посоветовали включить 2FA
- ряд советов в issue как проверить что вы установили проблемный пакет, как минимум стоит проверить что не запущен процесс
jsextensionПривет!
Сегодня стартовала Next.js conf, и вступительное видео посвящено релизу 12 версии Next.js, и планам на развитие инструмента.
Ссылка на это видео, вместе с расписанием и остальными записями - https://nextjs.org/conf/stage/keynote
Ссылка на релиз в блоге Next.js - https://nextjs.org/blog/next-12
Новые возможности Next.js очень круты и особенно вдохновляют меня как референс для развития https://tramvai.dev/!
Во-первых, был официально анонсирован
По последним релизам видно, что все больше возможностей swc становятся доступны в Next.js по умолчанию, а не за экспериментальными флагами.
Следующее изменение, опубликовано буквально несколько релизов назад -
Сначала я скептически отнесся к этой фиче, решил что это просто возможность переиспользовать логику для разных страниц приложения, с использованием не самого удачного паттерна.
Оказалось, что это гораздо более глобальная фича.
Новые миддлвары предназначены для деплоя и запуска на
Таким образом, вы можете разместить общую логику максимально близко к вашим пользователям, а ряд вещей, например редиректы, делать сразу в этих миддлварах, даже не нагружая приложение лишними запросами!
API этих миддлвар очень похоже на
Следующая фича - поддержка react@18 и React Server Components.
Помечена как beta, но примеры работы уже впечатляют.
React Server Components дают возможность делать запросы component-level, и точечно кэшировать результат рендеринга этих компонентов (для сравнения, обычно мы имеем два пути - персонализации нет, можно кэшировать всю страницу, или есть персонализация, и кэшировать практически нечего).
В tramvai планируем начинать интеграцию react@18 после его перехода в beta версию.
Классная фича - поддержка
Например, вы можете использовать пакеты без установки и бандлинга из
Впечатляет, правда пока не вижу насколько это широко применимо.
Добавлена опциональная поддержка
Также во вступительном видео рассказали про Vercel Live - https://vercel.com/live
Мне в первую очередь это интересно с технической стороны.
Этот инструмент использует ES модули для разработки, в качестве dev-сервера самописная разработка, а не Vite или Snowpack.
Мейнтейнеры обещают вынести этот сервер в open source - https://github.com/vercel/next.js/discussions/22406#discussioncomment-884453
Была представлена workflow интеграция Vercel Checks - https://vercel.com/integrations/checkly
Одна из возможностей - собирает Web Vitals метрики на каждый деплой приложения.
Тут у меня основной вопрос, какие погрешности дают такие замеры, кажется точному измерению перформанса приложения с клиентской стороны мешают очень много факторов.
Сегодня стартовала Next.js conf, и вступительное видео посвящено релизу 12 версии Next.js, и планам на развитие инструмента.
Ссылка на это видео, вместе с расписанием и остальными записями - https://nextjs.org/conf/stage/keynote
Ссылка на релиз в блоге Next.js - https://nextjs.org/blog/next-12
Новые возможности Next.js очень круты и особенно вдохновляют меня как референс для развития https://tramvai.dev/!
Во-первых, был официально анонсирован
swc в качестве транспилятора, минификатора и парсера CSS для styled-jsxswc уже достаточно давно проник в кодовую базу некста, также Vercel схантили мейнтейнера swc - https://twitter.com/kdy1devПо последним релизам видно, что все больше возможностей swc становятся доступны в Next.js по умолчанию, а не за экспериментальными флагами.
Следующее изменение, опубликовано буквально несколько релизов назад -
middleware для Next.js приложений.Сначала я скептически отнесся к этой фиче, решил что это просто возможность переиспользовать логику для разных страниц приложения, с использованием не самого удачного паттерна.
Оказалось, что это гораздо более глобальная фича.
Новые миддлвары предназначены для деплоя и запуска на
edge функциях https://vercel.com/features/edge-functions, и могут выполнять такие функции как аутентификация, A/B тестирование или стриминг ответа от Next.js сервера, и вообще все что придет вам в голову.Таким образом, вы можете разместить общую логику максимально близко к вашим пользователям, а ряд вещей, например редиректы, делать сразу в этих миддлварах, даже не нагружая приложение лишними запросами!
API этих миддлвар очень похоже на
Cloudfire Workers, которые в свою очередь вдохновлялись Service Worker API.Следующая фича - поддержка react@18 и React Server Components.
Помечена как beta, но примеры работы уже впечатляют.
React Server Components дают возможность делать запросы component-level, и точечно кэшировать результат рендеринга этих компонентов (для сравнения, обычно мы имеем два пути - персонализации нет, можно кэшировать всю страницу, или есть персонализация, и кэшировать практически нечего).
В tramvai планируем начинать интеграцию react@18 после его перехода в beta версию.
Классная фича - поддержка
URL Imports (помечена как alpha).Например, вы можете использовать пакеты без установки и бандлинга из
skypack CDN, а в демке https://www.youtube.com/watch?v=_WNeAubn92U импортируется компонент напрямую из Framer, и при изменении в Framer, компонент в приложении обновляется мгновенно.Впечатляет, правда пока не вижу насколько это широко применимо.
Добавлена опциональная поддержка
AVIF - что мы тоже планируем внедрять в tramvai приложения, тесты показывают отличные результаты + появилась поддержка в imgproxy - https://github.com/imgproxy/imgproxy/issues/456Также во вступительном видео рассказали про Vercel Live - https://vercel.com/live
Мне в первую очередь это интересно с технической стороны.
Этот инструмент использует ES модули для разработки, в качестве dev-сервера самописная разработка, а не Vite или Snowpack.
Мейнтейнеры обещают вынести этот сервер в open source - https://github.com/vercel/next.js/discussions/22406#discussioncomment-884453
Была представлена workflow интеграция Vercel Checks - https://vercel.com/integrations/checkly
Одна из возможностей - собирает Web Vitals метрики на каждый деплой приложения.
Тут у меня основной вопрос, какие погрешности дают такие замеры, кажется точному измерению перформанса приложения с клиентской стороны мешают очень много факторов.