Следующий кейс связан с переходом на async скрипты - https://t.me/super_oleg_dev/133
Используя defer скрипты, у вас есть уверенность - они будут выполнены когда DOM завершён (безопасно для гидрации) и по порядку.
Первое что я пропустил - это async для скрипта с полифиллами.
Трамвай использует подход с динамической загрузкой чанка с полифиллами - https://philipwalton.com/articles/loading-polyfills-only-when-needed/
При переходе на async мы больше не уверены что полифиллы будут загружены до выполнения основного кода.
Пришлось убрать атрибут defer у скрипта, что неизбежно приведет к штрафу на перформанс, из-за блокирующей загрузки. Ищу идеи получше)
Вторая проблема связана с нашими микрофронтами Child Apps.
Если в скриптах приложения сборщик отвечает за порядок их выполнения, проблем нет даже с async скриптами, так как есть явная связь, например через webpack_require.
Но скрипты микрофронтов добавляются на страницу отдельно, если они были предзагружены на сервере, прямой связи нет.
На клиенте скрипт микрофронта сохраняет его в window в уникальную переменную.
И клиентский код инициализации ожидает наличие этого микрофонта в window, так как скрипт есть на странице - ведь с defer он был бы точно выполнен заранее.
Для решения проблемы пришлось добавить логику ожидания загрузки скрипта - точки входа микрофронта, на события load и error.
И при этом предусмотреть кейс когда он был загружен или упал до навешивания обработчиков этих событий - добавить атрибут loaded, и его проставление true/false инлайн в атрибутах onload/onerror - у тега script нет никакого свойства что бы узнать текущее состояние загрузки скрипта.
При этом архитектурно не должно было поменяться поведение, что догрузка микрофронта на клиенте не должна задерживать инициализацию и гидрацию приложения - как с кейсами для Child Apps которые упали на сервере или их забыли предзагрузить.
Suspense + Error Boundary позволяет показать фаллбэк в каждом проблемном кейсе до итоговой успешной загрузки микрофронта.
Используя defer скрипты, у вас есть уверенность - они будут выполнены когда DOM завершён (безопасно для гидрации) и по порядку.
Первое что я пропустил - это async для скрипта с полифиллами.
Трамвай использует подход с динамической загрузкой чанка с полифиллами - https://philipwalton.com/articles/loading-polyfills-only-when-needed/
При переходе на async мы больше не уверены что полифиллы будут загружены до выполнения основного кода.
Пришлось убрать атрибут defer у скрипта, что неизбежно приведет к штрафу на перформанс, из-за блокирующей загрузки. Ищу идеи получше)
Вторая проблема связана с нашими микрофронтами Child Apps.
Если в скриптах приложения сборщик отвечает за порядок их выполнения, проблем нет даже с async скриптами, так как есть явная связь, например через webpack_require.
Но скрипты микрофронтов добавляются на страницу отдельно, если они были предзагружены на сервере, прямой связи нет.
На клиенте скрипт микрофронта сохраняет его в window в уникальную переменную.
И клиентский код инициализации ожидает наличие этого микрофонта в window, так как скрипт есть на странице - ведь с defer он был бы точно выполнен заранее.
Для решения проблемы пришлось добавить логику ожидания загрузки скрипта - точки входа микрофронта, на события load и error.
И при этом предусмотреть кейс когда он был загружен или упал до навешивания обработчиков этих событий - добавить атрибут loaded, и его проставление true/false инлайн в атрибутах onload/onerror - у тега script нет никакого свойства что бы узнать текущее состояние загрузки скрипта.
При этом архитектурно не должно было поменяться поведение, что догрузка микрофронта на клиенте не должна задерживать инициализацию и гидрацию приложения - как с кейсами для Child Apps которые упали на сервере или их забыли предзагрузить.
Suspense + Error Boundary позволяет показать фаллбэк в каждом проблемном кейсе до итоговой успешной загрузки микрофронта.
Telegram
SuperOleg dev notes
Теперь фреймворк умеет в потоковый рендеринг, появились deferred экшены, и Await компонент для использования этих экшенов вместе с Suspense.
Большая часть работы сделана, проблемы как всегда в деталях.
Тут очередная хвала React Working Group, а именно гайду…
Большая часть работы сделана, проблемы как всегда в деталях.
Тут очередная хвала React Working Group, а именно гайду…
🤔4👍2
Привет!
Обновлял библиотеку для клиентского performance мониторинга perfume.js, и как раз добавились изменения по метрике Time To First Byte - теперь без модификаций передается значение из пакета web-vitals, который используется под капотом (раньше по какой-то причине вычиталось время
По какой логике вычисляется TTFB хорошо описано тут - https://stackoverflow.com/questions/69116839/does-ttfb-include-the-time-for-the-request-that-redirects-to-my-page
Вспомнил про это, так как после релиза одного из обновившихся приложений метрики TTFB на графике сильно ухудшились - раньше эта метрика гораздо лучше соотносилась с временем ответа SSR сервера приложения, а сейчас же стала значительно выше.
Но так как метрика просто стала корректной и надо принять как факт, что именно так ее измеряют инструменты гугла (CrUX, web-vitals), стало интересно - а почему такая разница с временем ответа SSR?
Решил разобрать прочие метрики, которые вычисляем из NavigationTiming API, кастомных пока нет, берем что есть из perfume.js, не идеально но полезные инсайты есть.
Обновлял библиотеку для клиентского performance мониторинга perfume.js, и как раз добавились изменения по метрике Time To First Byte - теперь без модификаций передается значение из пакета web-vitals, который используется под капотом (раньше по какой-то причине вычиталось время
requestStart
).requestStart
- это одна из performance метрик браузера относительно времени загрузки HTML документа, которые можно получить через PerformanceNavigationTiming API.По какой логике вычисляется TTFB хорошо описано тут - https://stackoverflow.com/questions/69116839/does-ttfb-include-the-time-for-the-request-that-redirects-to-my-page
Вспомнил про это, так как после релиза одного из обновившихся приложений метрики TTFB на графике сильно ухудшились - раньше эта метрика гораздо лучше соотносилась с временем ответа SSR сервера приложения, а сейчас же стала значительно выше.
Но так как метрика просто стала корректной и надо принять как факт, что именно так ее измеряют инструменты гугла (CrUX, web-vitals), стало интересно - а почему такая разница с временем ответа SSR?
Решил разобрать прочие метрики, которые вычисляем из NavigationTiming API, кастомных пока нет, берем что есть из perfume.js, не идеально но полезные инсайты есть.
GitHub
GitHub - Zizzamia/perfume.js: Web performance library for measuring all performance vitals metrics
Web performance library for measuring all performance vitals metrics - Zizzamia/perfume.js
👍4🔥3
И сразу пример, как отличается TTFB от времени ответа сервера, это графики для desktop пользователей на 75 перцентиле одного из приложений - при времени ответа в среднем в 400мс, TTFB выше в полтора-два раза!
Первое, не значительное но интересное - это время работы Service Worker.
Как я понимаю, разница между точками workerStart и fetchStart с предыдущей диаграммы - это время на инициализацию спящего SW + время которое он потратил на "fetch" обработчик перед запросом текущей страницы.
Ночью и рано утром событий мало и шум сильный, а днем видно что в среднем оверхед небольшой - 10-20мс даже на мобилках, на десктопе еще меньше.
Но тем не менее, эти расходы есть, а высокие перцентили и пики в ночные часы показывают, что на небольшое количество пользователей оверхэд в разы сильнее.
Тут нам стоит провести эксперимент с Navigation Preload API, что бы делать запрос за страницей параллельно активации SW - https://web.dev/blog/navigation-preload
А в будущем возможно поможет Static Routing API - https://developer.chrome.com/blog/service-worker-static-routing
Как я понимаю, разница между точками workerStart и fetchStart с предыдущей диаграммы - это время на инициализацию спящего SW + время которое он потратил на "fetch" обработчик перед запросом текущей страницы.
Ночью и рано утром событий мало и шум сильный, а днем видно что в среднем оверхед небольшой - 10-20мс даже на мобилках, на десктопе еще меньше.
Но тем не менее, эти расходы есть, а высокие перцентили и пики в ночные часы показывают, что на небольшое количество пользователей оверхэд в разы сильнее.
Тут нам стоит провести эксперимент с Navigation Preload API, что бы делать запрос за страницей параллельно активации SW - https://web.dev/blog/navigation-preload
А в будущем возможно поможет Static Routing API - https://developer.chrome.com/blog/service-worker-static-routing
👍2
Если в приложении есть фон серверных редиректов - его оверхэд посчитать и заметить сложно - пример графиков с пустым 75 перцентилем и с 95 перцентилем, где уже видим что редиректы не бесплатные.
К примеру на tinkoff.ru все приложения будут делать редирект на адрес с "/" в конце.
Но из-за того что редиректов не больше 10% от общего числа запросов, нулевые метрики забивают все остальное.
Возможно стоит сделать отдельный график с отфильтрованными нулевыми значениями?
Метрика - время между redirectStart и redirectEnd.
К примеру на tinkoff.ru все приложения будут делать редирект на адрес с "/" в конце.
Но из-за того что редиректов не больше 10% от общего числа запросов, нулевые метрики забивают все остальное.
Возможно стоит сделать отдельный график с отфильтрованными нулевыми значениями?
Метрика - время между redirectStart и redirectEnd.
👍2
Приятно удивляет время резолва DNS - метрика между domainLookupStart и domainLookupEnd.
До 95 перцентиля (на первой картинке) даже не видно что это время вообще есть - как мне кажется, браузер либо хорошо кэширует результат резолва, либо делает его предварительно - например во время ввода урла в адресной строке, и в метрику уже это время не попадает.
На 99 перцентиле (второе изображение) уже видно, что кто-то из юзеров сталкивается с резолвом DNS, но скорее всего пики связаны с плохим соединением у конкретного клиента.
До 95 перцентиля (на первой картинке) даже не видно что это время вообще есть - как мне кажется, браузер либо хорошо кэширует результат резолва, либо делает его предварительно - например во время ввода урла в адресной строке, и в метрику уже это время не попадает.
На 99 перцентиле (второе изображение) уже видно, что кто-то из юзеров сталкивается с резолвом DNS, но скорее всего пики связаны с плохим соединением у конкретного клиента.
👍1
А вот время на установку TCP и TLS соединения тратят уже большинство пользователей, чем хуже интернет и больше Round Trip Time, тем хуже будет метрика у клиентов.
Тут минус, что perfume.js из коробки не собирает это время и вычисляю косвенно TCP + TLS - но на самом деле Navigation Timing API позволяет собрать их по отдельности.
Это может быть полезно для идентификации проблем с TLS, например какая-то медленная цепочка сертификатов (не сталкивался с таким на практике).
Тут минус, что perfume.js из коробки не собирает это время и вычисляю косвенно TCP + TLS - но на самом деле Navigation Timing API позволяет собрать их по отдельности.
Это может быть полезно для идентификации проблем с TLS, например какая-то медленная цепочка сертификатов (не сталкивался с таким на практике).
👍1
И последняя, но к сожалению самая значимая часть - это время скачивания HTML документа, метрика между responseStart и responseEnd.
Не зря Qwik работает над уменьшением HTML разметки при стриминге - https://www.builder.io/blog/qwik-2-coming-soon
Типичный SSR с React, в нашем случае на Tramvai но мы тут не исключение, создает очень большой payload - это и большое количество информации в head (мета-теги, скрипты, preload теги), и огромное количество тегов в body, и HTTP заголовки вроде cookie и CSP.
Также, значительная часть HTML - это сериализованный стейт, переданный с сервера на клиент.
Концепт Qwik с resumability как раз хорош тем, что в HTML нет дублирующей информации, только то что нужно клиентскому коду для продолжения работы в браузере (для реакта же мы должны иметь серверный стейт для корректной гидрации всего дерева).
Даже с сжатием HTML на стороне балансера, эта часть на мобилках может занимать столько же времени, сколько отвечает сервер.
Не зря Qwik работает над уменьшением HTML разметки при стриминге - https://www.builder.io/blog/qwik-2-coming-soon
Типичный SSR с React, в нашем случае на Tramvai но мы тут не исключение, создает очень большой payload - это и большое количество информации в head (мета-теги, скрипты, preload теги), и огромное количество тегов в body, и HTTP заголовки вроде cookie и CSP.
Также, значительная часть HTML - это сериализованный стейт, переданный с сервера на клиент.
Концепт Qwik с resumability как раз хорош тем, что в HTML нет дублирующей информации, только то что нужно клиентскому коду для продолжения работы в браузере (для реакта же мы должны иметь серверный стейт для корректной гидрации всего дерева).
Даже с сжатием HTML на стороне балансера, эта часть на мобилках может занимать столько же времени, сколько отвечает сервер.
👍4
Подведу некий summary:
- Round Trip Time важен на каждом из многочисленных этапов загрузки HTML, в идеальном мире точка входа в приложение - это CDN
- Редиректы не бесплатные как для клиента, так и для приложения, лучше их не делать или делать поближе к пользователю, например на уровне балансера
- Service Worker может как ускорить, так и замедлить ваше приложение - измеряйте все что можете, пробуйте Navigation Preload, не используйте SW для "галочки" - браузер и так отлично все кэширует
- Раздутый HTML и плохой интернет - это значительная проблема для SSR, которую трудно исправить малыми усилиями
Круто, что есть много браузерных API для мониторинга таких деталей.
- Round Trip Time важен на каждом из многочисленных этапов загрузки HTML, в идеальном мире точка входа в приложение - это CDN
- Редиректы не бесплатные как для клиента, так и для приложения, лучше их не делать или делать поближе к пользователю, например на уровне балансера
- Service Worker может как ускорить, так и замедлить ваше приложение - измеряйте все что можете, пробуйте Navigation Preload, не используйте SW для "галочки" - браузер и так отлично все кэширует
- Раздутый HTML и плохой интернет - это значительная проблема для SSR, которую трудно исправить малыми усилиями
Круто, что есть много браузерных API для мониторинга таких деталей.
🔥9👍5
Забыл про API, которое разочаровало (по крайней мере не очень работает для нас) - Network Information API
По собранным метрикам, у подавляющего большинства пользователей отличный интернет, большая пропускная способность, некий средний RTT - в общем пользы не больше чем от метрики First Input Delay.
По собранным метрикам, у подавляющего большинства пользователей отличный интернет, большая пропускная способность, некий средний RTT - в общем пользы не больше чем от метрики First Input Delay.
Парочка интересных кейсы из беты React 19, которые особо не освещались:
- Прощай useIsomorphicLayoutEffect, больше нет варнинга на сервере - https://github.com/facebook/react/pull/26395
- Если вы используете throw promise или либы с поддержкой Suspense, в рамках одного Suspense загрузка данных в параллельных компонентах начнет происходить последовательно - https://github.com/facebook/react/pull/26380
Пример кода где будет водопад запросов при использовании условного useSuspenseQuery:
В релизе очень порадовало улучшение ошибок гидрации, и централизованная обработка ошибок Error Boundaries.
Также я никак не пойму в какой версии удалили или удалят 421 ошибку гидрации - https://github.com/facebook/react/issues/24959#issuecomment-1317309116
Ошибка происходит при ререндере Suspense компонента, поддерево которого не завершило гидрацию, и приводит к деоптимизации - клиентский рендер вместо гидрации.
Очень легко словить такую ошибку используя useSyncExternalStore.
Потратил часы на разборы таких ошибок, а ситуация в итоге странная - ошибку выпиливают, а деоптимизация остаётся.
Наверное мне стоило более тщательно подойти к замеру разницы в перформансе при деоптимизации, может проблема и не такая значительная, раз ее просто можно заглушить?
- Прощай useIsomorphicLayoutEffect, больше нет варнинга на сервере - https://github.com/facebook/react/pull/26395
- Если вы используете throw promise или либы с поддержкой Suspense, в рамках одного Suspense загрузка данных в параллельных компонентах начнет происходить последовательно - https://github.com/facebook/react/pull/26380
Пример кода где будет водопад запросов при использовании условного useSuspenseQuery:
const Root = () => {
return <>
<Suspense>
<CmpWithUseSuspenseQuery />
<CmpWithUseSuspenseQuery />
</Suspense>
</>
}
В релизе очень порадовало улучшение ошибок гидрации, и централизованная обработка ошибок Error Boundaries.
Также я никак не пойму в какой версии удалили или удалят 421 ошибку гидрации - https://github.com/facebook/react/issues/24959#issuecomment-1317309116
Ошибка происходит при ререндере Suspense компонента, поддерево которого не завершило гидрацию, и приводит к деоптимизации - клиентский рендер вместо гидрации.
Очень легко словить такую ошибку используя useSyncExternalStore.
Потратил часы на разборы таких ошибок, а ситуация в итоге странная - ошибку выпиливают, а деоптимизация остаётся.
Наверное мне стоило более тщательно подойти к замеру разницы в перформансе при деоптимизации, может проблема и не такая значительная, раз ее просто можно заглушить?
GitHub
Remove layout effect warning on the server by rickhanlonii · Pull Request #26395 · facebook/react
Overview
This PR unfortunately removes the warning emitted when using layout effects on the server:
useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server...
This PR unfortunately removes the warning emitted when using layout effects on the server:
useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server...
👍14🔥1
Forwarded from Веб-платформа
Собрал для вас в одну папку авторов, ведущих блоги по фронтенду, веб-разработке и вокруг неё.
🔗 https://t.me/addlist/Z6Efi4jXwe9lODcy
Специально отобрал именно ламповые авторские блоги, а не перепосты ссылок, документации или тексты не про код.
Для меня это хороший способ следить за реакциями о событиях индустрии. Например, конференции проходят не так часто, и контент с них становится общедоступным не сразу. Кроме того, там обычно рассказывают о чём-то эпичном, не сиюминутном, так как формат обязывает. А вот в авторских бложиках (и комментариях к постам) можно собрать цельную картинку происходящего в рутине, и не просто факты, а именно авторскую интерпретацию и комментарии.
Надеюсь, будет полезно ✌️
@web_platform | Поддержать канал 🤝
🔗 https://t.me/addlist/Z6Efi4jXwe9lODcy
Специально отобрал именно ламповые авторские блоги, а не перепосты ссылок, документации или тексты не про код.
Для меня это хороший способ следить за реакциями о событиях индустрии. Например, конференции проходят не так часто, и контент с них становится общедоступным не сразу. Кроме того, там обычно рассказывают о чём-то эпичном, не сиюминутном, так как формат обязывает. А вот в авторских бложиках (и комментариях к постам) можно собрать цельную картинку происходящего в рутине, и не просто факты, а именно авторскую интерпретацию и комментарии.
Надеюсь, будет полезно ✌️
@web_platform | Поддержать канал 🤝
❤12👍4
Привет!
Свежий кейс отладки, вряд ли будет полезен (специфичный), но хочется его проговорить и забыть.
В Tramvai есть своя реализация микрофронтов с поддержкой Module Federation - Child Apps.
Какое-то время назад добавлял для Child Apps поддержку разделения на страницы для разных роутов, значительная часть работы заключалась в интеграции @loadable.
Так как микрофронты универсальные - используются на сервере и на клиенте, для эффективного добавления всех JS и CSS файлов на страницу при серверном рендеринге, нужно иметь список этих файлов.
Для этого мы уже использовали Module Federation ChunkCorrelationPlugin, который генерирует специальный json файл, содержащий список shared чанков, необходимых для микрофронта. Это как бы stats, но не тот stats что используется в webpack.
При использовании @loadable, нам нужно также иметь информацию о созданных через динамический импорт чанков, которую ChunkCorrelationPlugin не предоставляет.
Решил эту проблему через генерацию дополнительного
Но можно и заморочиться и через кастомный плагин самостоятельно сгенерировать единый файл, который будет содержать информацию из обоих миров.
Итак (это еще присказка), добавили @loadable и динамические чанки для отдельных страниц, но получили дублирование общего кода в каждом чанке - так как при использовании Module Federation как правило отключают splitChunks, и генерируются конкретные файлы:
- точка входа в микрофронт (в нашем случае серверная и клиентская)
- сам код микрофронта
- чанки для shared зависимостей
На самом деле добавить splitChunks оказалось можно, главное аккуратно - все shared зависимости, указанные для Module Federation, должны быть исключены из группы, в которую попадут общие модули между страницами, разделенными через @loadable - по сути все модули которые тянут async чанки.
И сам конфиг splitChunks (использует подход granular chunking)
Итак, вернемся к отладке и непосредственно багу.
У нас есть матрица тестов Child Apps для проверки совместимости разных мажорных версий хостового приложения и микрофронтов.
Что бы тестировать один и тот же код приложения и микрофронтов, используем симлинки.
По не понятной до сих пор причине один тест начал падать, и показывать какие-то странные новые чанки, собранные для конкретного микрофронта, с названием содержащим webpack_sharing_consume.
Для сборки приложения и микрофронта через @tramvai/cli можно указать параметр "resolveSymlinks: false", который прокинет соответствующее значение в конфиг вебпака - и он как раз использовался в интеграционных тестах.
Также симлинки использует yarn workspaces, через который организована работа с пакетами фреймворка в нашей монорепе.
Отладку мне сильно попортили наши настройки webpack file-system cache, которые не учитывали этот параметр, и переиспользовался кэш с любым значением флага, а я соответственно получал не стабильные результаты.
Оказалось, что для получения списка shared модулей использовался
А в splitChunks в метод проверки вхождения в группу в название модуля прилетал путь симлинки которую делал yarn (с node_modules). Пример -
Дополнительно заиспользовал пакет resolve для вычисления пути до модуля без резолва симлинки, что бы закрыть и кейсы пользователей, и в нашей монорепе.
История получилась в итоге не совсем про отладку, но раз с этого начал, добавлю вывод - при отладке сборки выключайте кэши)
Свежий кейс отладки, вряд ли будет полезен (специфичный), но хочется его проговорить и забыть.
В Tramvai есть своя реализация микрофронтов с поддержкой Module Federation - Child Apps.
Какое-то время назад добавлял для Child Apps поддержку разделения на страницы для разных роутов, значительная часть работы заключалась в интеграции @loadable.
Так как микрофронты универсальные - используются на сервере и на клиенте, для эффективного добавления всех JS и CSS файлов на страницу при серверном рендеринге, нужно иметь список этих файлов.
Для этого мы уже использовали Module Federation ChunkCorrelationPlugin, который генерирует специальный json файл, содержащий список shared чанков, необходимых для микрофронта. Это как бы stats, но не тот stats что используется в webpack.
При использовании @loadable, нам нужно также иметь информацию о созданных через динамический импорт чанков, которую ChunkCorrelationPlugin не предоставляет.
Решил эту проблему через генерацию дополнительного
stats_loadable.json
файла с помощью плагина @loadable/webpack-plugin. На сервере не проблема запросить доп. файл для каждого микрофронта, так как мы кэшируем эти запросы.Но можно и заморочиться и через кастомный плагин самостоятельно сгенерировать единый файл, который будет содержать информацию из обоих миров.
Итак (это еще присказка), добавили @loadable и динамические чанки для отдельных страниц, но получили дублирование общего кода в каждом чанке - так как при использовании Module Federation как правило отключают splitChunks, и генерируются конкретные файлы:
- точка входа в микрофронт (в нашем случае серверная и клиентская)
- сам код микрофронта
- чанки для shared зависимостей
На самом деле добавить splitChunks оказалось можно, главное аккуратно - все shared зависимости, указанные для Module Federation, должны быть исключены из группы, в которую попадут общие модули между страницами, разделенными через @loadable - по сути все модули которые тянут async чанки.
И сам конфиг splitChunks (использует подход granular chunking)
Итак, вернемся к отладке и непосредственно багу.
У нас есть матрица тестов Child Apps для проверки совместимости разных мажорных версий хостового приложения и микрофронтов.
Что бы тестировать один и тот же код приложения и микрофронтов, используем симлинки.
По не понятной до сих пор причине один тест начал падать, и показывать какие-то странные новые чанки, собранные для конкретного микрофронта, с названием содержащим webpack_sharing_consume.
Для сборки приложения и микрофронта через @tramvai/cli можно указать параметр "resolveSymlinks: false", который прокинет соответствующее значение в конфиг вебпака - и он как раз использовался в интеграционных тестах.
Также симлинки использует yarn workspaces, через который организована работа с пакетами фреймворка в нашей монорепе.
Отладку мне сильно попортили наши настройки webpack file-system cache, которые не учитывали этот параметр, и переиспользовался кэш с любым значением флага, а я соответственно получал не стабильные результаты.
Оказалось, что для получения списка shared модулей использовался
require.resolve
, который симлинки всегда резолвит, и отдает реальный путь. Пример - tramvai/packages/modules/react-query/lib/index.js
А в splitChunks в метод проверки вхождения в группу в название модуля прилетал путь симлинки которую делал yarn (с node_modules). Пример -
tramvai/node_modules/@tramvai/module-react-query/lib/index.js
Дополнительно заиспользовал пакет resolve для вычисления пути до модуля без резолва симлинки, что бы закрыть и кейсы пользователей, и в нашей монорепе.
История получилась в итоге не совсем про отладку, но раз с этого начал, добавлю вывод - при отладке сборки выключайте кэши)
tramvai.dev
Overview | tramvai
Micro frontends heavily integrated with tramvai framework
❤12👍3
Почему я люблю React и считаю его развитие последовательным, гармоничным и в целом работой крутых инженеров, которые думают о своих пользователях. Что именно сделало меня лучше как инженера. И конечно что я считаю минусами фреймворка либо экосистемы.
Начну наверное с Fiber архитектуры - а именно переход React на Fiber является фундаментом, благодаря которому возможны последующие фичи, такие как хуки и Suspense, Concurrent Rendering и Selective Hydration, React Server Components и Partial Prerendering.
Fiber открыл ряд возможностей, например:
- рендерить компоненты по отдельности и с разным приоритетом
- ставить работу на паузу и возобновлять ее
Это действительно крутое стратегическое решения для архитектуры React (много ли вещей вы продумали в своей работе на 8+ лет вперед?), и что немаловажно, нацеленное на лучший UX, так как длинные блокирующие задачи это одна из самых значимых проблем производительности веб-приложений.
Лучше про перф за меня расскажет Ден в этом докладе - https://www.youtube.com/watch?v=nLF0n9SACd4&ab_channel=JSConf.
Я встречал и критикау демок от React тимы с Suspense и конкурентным рендерингом и приоритезацией апдейтов на пользовательский ввод - что либо это не real world кейсы, либо перфа можно достичь и другими способами.
Не знаю, я тут вижу только крутые фичи у которых простая концептуальная база - разделение больших задач на более мелкие.
Можем обсудить в комментариях)
Следующий майндшифт - это хуки. Раньше у нас были для работы с состоянием и контекстом классовые компоненты (миксины я практически не застал), с относительно сложным жизненным циклом и действительно сложными механизмами для синхронизации стейта и пропсов, и отсутствием адекватного механизма для переиспользования логики (всей душой не люблю паттерн с композицией пачки HOC'ов).
Появление хуков - это естественная эволюция реакта, они можно сказать "напрашивались". На самом деле, конечно я не знал как будут выглядеть хуки до их анонса. Но именно концепция хуков очень органично вписывается в построение интерфейсов на функциональных React компонентах.
Что мы получили:
- простой цикл обновления компонента (useEffect подписка/очистка)
- и также предсказуемый (массив зависимостей)
- удобная работа со стейтом и контекстом
- нативный механизм для переиспользования логики
Но это возможно дело случая - для меня хуки сразу вписались в ментальную модель, которую я держу в голове разрабатывая на React, даже до начала их практического использования. У других разработчиков хуки вызвали отторжение. В общем как с любой другой новой технологией (из свежего это сигналы в Angular, руны в Svelte).
Также репутацию хуков подпортили попытки инкапсулировать в них бизнес-логику.
Мне кажется, кастомные хуки это про два основных кейса:
- переиспользовать UI логику, когда работаем с ref либо с DOM деревом напрямую
- переиспользовать утилитарную логику, общие кейсы работы с состоянием или жизненным циклом (например useSelector, useQuery, useToggle, useTimeout)
Начну наверное с Fiber архитектуры - а именно переход React на Fiber является фундаментом, благодаря которому возможны последующие фичи, такие как хуки и Suspense, Concurrent Rendering и Selective Hydration, React Server Components и Partial Prerendering.
Fiber открыл ряд возможностей, например:
- рендерить компоненты по отдельности и с разным приоритетом
- ставить работу на паузу и возобновлять ее
Это действительно крутое стратегическое решения для архитектуры React (много ли вещей вы продумали в своей работе на 8+ лет вперед?), и что немаловажно, нацеленное на лучший UX, так как длинные блокирующие задачи это одна из самых значимых проблем производительности веб-приложений.
Лучше про перф за меня расскажет Ден в этом докладе - https://www.youtube.com/watch?v=nLF0n9SACd4&ab_channel=JSConf.
Я встречал и критикау демок от React тимы с Suspense и конкурентным рендерингом и приоритезацией апдейтов на пользовательский ввод - что либо это не real world кейсы, либо перфа можно достичь и другими способами.
Не знаю, я тут вижу только крутые фичи у которых простая концептуальная база - разделение больших задач на более мелкие.
Можем обсудить в комментариях)
Следующий майндшифт - это хуки. Раньше у нас были для работы с состоянием и контекстом классовые компоненты (миксины я практически не застал), с относительно сложным жизненным циклом и действительно сложными механизмами для синхронизации стейта и пропсов, и отсутствием адекватного механизма для переиспользования логики (всей душой не люблю паттерн с композицией пачки HOC'ов).
Появление хуков - это естественная эволюция реакта, они можно сказать "напрашивались". На самом деле, конечно я не знал как будут выглядеть хуки до их анонса. Но именно концепция хуков очень органично вписывается в построение интерфейсов на функциональных React компонентах.
Что мы получили:
- простой цикл обновления компонента (useEffect подписка/очистка)
- и также предсказуемый (массив зависимостей)
- удобная работа со стейтом и контекстом
- нативный механизм для переиспользования логики
Но это возможно дело случая - для меня хуки сразу вписались в ментальную модель, которую я держу в голове разрабатывая на React, даже до начала их практического использования. У других разработчиков хуки вызвали отторжение. В общем как с любой другой новой технологией (из свежего это сигналы в Angular, руны в Svelte).
Также репутацию хуков подпортили попытки инкапсулировать в них бизнес-логику.
Мне кажется, кастомные хуки это про два основных кейса:
- переиспользовать UI логику, когда работаем с ref либо с DOM деревом напрямую
- переиспользовать утилитарную логику, общие кейсы работы с состоянием или жизненным циклом (например useSelector, useQuery, useToggle, useTimeout)
GitHub
GitHub - acdlite/react-fiber-architecture: A description of React's new core algorithm, React Fiber
A description of React's new core algorithm, React Fiber - acdlite/react-fiber-architecture
👍20💩7❤4🥴4
Отвлечемся поговорить про перформанс.
Наверное каждый эксперт в отладке и ускорении React приложений прошел непростой путь оптимизации на проде многоступенчатых форм или сложных таблиц, и знает все способы как избежать лишних ререндеров (которые не проблема сами по себе, но проблема в масштабе).
И каждый эксперт скорее всего не хотел бы этого знать и об этом думать.
Я согласен, что механизм обновления в React по умолчанию заставляет нас делать больше работы и предварительных оптимизаций (а дефолты это очень важно), либо не делать и потом все-таки становиться экспертами по реакт перформансу.
Это не означает, что в других фреймворках ничего не нужно оптимизировать, проблемы зачастую в пользовательском коде. Но по умолчанию - да, тормоза в React приложении поймать легче. При этом, мы практически всегда знаем что с этим делать.
Можно рассматривать эту проблему как сравнение Virtual DOM против реактивности и сигналов.
Пару ссылок про проблемы Virtual DOM:
- https://svelte.dev/blog/virtual-dom-is-pure-overhead
- https://vuejs.org/guide/extras/rendering-mechanism#compiler-informed-virtual-dom
Но не будем забывать про плюсы, то что уже обсуждали в предыдущем посте. Благодаря Virtual DOM и Fiber архитектуре, в реакте реализовыван прерываемый рендеринг, который открывает пачку крутых UX паттернов (suspense, транзишены и так далее).
Отличный обзор concurrent фич от Ивана Акулова - https://3perf.com/talks/react-concurrency/
Если говорить про другие фреймворки, интересные мысли можно почитать по ключевым словам типа Suspense на гитхабе в соответствующих проектах, например:
- https://github.com/sveltejs/svelte/issues/1736
- https://github.com/sveltejs/svelte/issues/3203#issuecomment-797346259
И мысли разные, в том числе от авторов фреймворков - что-то реализовать можно, что-то сложно, что-то не нужно.
Как и везде, серебряной пули нет, а плохие и медленные приложения писать с использованием сигналов также легко.
Также одна из важных вещей в Реакт - консистентность состояния.
Итого:
- по умолчанию в React легко написать медленный код
- это решаемая проблема, но бойлерплейт/надо думать
- текущая архитектура с Virtual DOM имеет и преимущества над другими решениями (компилируемыми / реактивными)
Добавлю еще коротко про React Forget.
Концептуально все просто - поможет оставить исходный код чистым, а продакшн код производительным.
Считаю это крутым экспериментальным проектом, надеюсь на его успех.
И это точно не менее предсказуемый инструмент чем любой compile-time фреймворк (такие мнения тоже встречал).
Наверное каждый эксперт в отладке и ускорении React приложений прошел непростой путь оптимизации на проде многоступенчатых форм или сложных таблиц, и знает все способы как избежать лишних ререндеров (которые не проблема сами по себе, но проблема в масштабе).
И каждый эксперт скорее всего не хотел бы этого знать и об этом думать.
Я согласен, что механизм обновления в React по умолчанию заставляет нас делать больше работы и предварительных оптимизаций (а дефолты это очень важно), либо не делать и потом все-таки становиться экспертами по реакт перформансу.
Это не означает, что в других фреймворках ничего не нужно оптимизировать, проблемы зачастую в пользовательском коде. Но по умолчанию - да, тормоза в React приложении поймать легче. При этом, мы практически всегда знаем что с этим делать.
Можно рассматривать эту проблему как сравнение Virtual DOM против реактивности и сигналов.
Пару ссылок про проблемы Virtual DOM:
- https://svelte.dev/blog/virtual-dom-is-pure-overhead
- https://vuejs.org/guide/extras/rendering-mechanism#compiler-informed-virtual-dom
Но не будем забывать про плюсы, то что уже обсуждали в предыдущем посте. Благодаря Virtual DOM и Fiber архитектуре, в реакте реализовыван прерываемый рендеринг, который открывает пачку крутых UX паттернов (suspense, транзишены и так далее).
Отличный обзор concurrent фич от Ивана Акулова - https://3perf.com/talks/react-concurrency/
Если говорить про другие фреймворки, интересные мысли можно почитать по ключевым словам типа Suspense на гитхабе в соответствующих проектах, например:
- https://github.com/sveltejs/svelte/issues/1736
- https://github.com/sveltejs/svelte/issues/3203#issuecomment-797346259
И мысли разные, в том числе от авторов фреймворков - что-то реализовать можно, что-то сложно, что-то не нужно.
Как и везде, серебряной пули нет, а плохие и медленные приложения писать с использованием сигналов также легко.
Также одна из важных вещей в Реакт - консистентность состояния.
Итого:
- по умолчанию в React легко написать медленный код
- это решаемая проблема, но бойлерплейт/надо думать
- текущая архитектура с Virtual DOM имеет и преимущества над другими решениями (компилируемыми / реактивными)
Добавлю еще коротко про React Forget.
Концептуально все просто - поможет оставить исходный код чистым, а продакшн код производительным.
Считаю это крутым экспериментальным проектом, надеюсь на его успех.
И это точно не менее предсказуемый инструмент чем любой compile-time фреймворк (такие мнения тоже встречал).
svelte.dev
Virtual DOM is pure overhead
Let’s retire the ‘virtual DOM is fast’ myth once and for all
👍15❤3🔥1