Релиз Vite 6
Вышел релиз Vite 6 – самое значительное обновление после Vite 2. В новой версии появилось экспериментальное Environment API. Это новое API в основном предназначено для авторов фреймворков. С его помощью можно предлагать разрабатывать локально приложения в условиях приближенных к продакшену. Если вы разрабатываете SPA приложение, то для вас ничего не поменяется. Даже если вы разрабатываете SSR приложение, то Vite 6 имеет обратную совместимость. Основная целевая аудитория Environment API — авторы фреймворков.
Из других изменений – расширение HTML элементов, которые могут ссылаться на asset для обработки и бандлинга в Vite. Например, <audio src>, <video src> и другие.
Для большинства проектов обновление до 6ой версии не должно вызвать затруднений.
https://vite.dev/blog/announcing-vite6.html
Вышел релиз Vite 6 – самое значительное обновление после Vite 2. В новой версии появилось экспериментальное Environment API. Это новое API в основном предназначено для авторов фреймворков. С его помощью можно предлагать разрабатывать локально приложения в условиях приближенных к продакшену. Если вы разрабатываете SPA приложение, то для вас ничего не поменяется. Даже если вы разрабатываете SSR приложение, то Vite 6 имеет обратную совместимость. Основная целевая аудитория Environment API — авторы фреймворков.
Из других изменений – расширение HTML элементов, которые могут ссылаться на asset для обработки и бандлинга в Vite. Например, <audio src>, <video src> и другие.
Для большинства проектов обновление до 6ой версии не должно вызвать затруднений.
https://vite.dev/blog/announcing-vite6.html
👍19❤1
Как работает React Island
React Island – это подход внедрения React компонентов и даже целых приложений в статическую HTML страницу. Этот подход можно использовать для постепенного рефакторинга статического сайта или, если вы хотите добавить небольшую интерактивную часть на сайт, оставить все остальное как есть статическим.
Как это работает:
1) Рендерите HTML любым способом
2) Добавляете корневой div в HTML для React
3) Вызываете функцию
4) Рендерите небольшое React приложение в добавленный div элемент
React Island подойдет если у вас есть сайт, который рендерится статически через шаблонизатор. Автор предлагает не переписывать все сразу на React, т.к. на это может уйти много времени, а вместо этого делать постепенный рефакторинг. Либо оставить все как есть и добавлять небольшую интерактивность частями.
Для реализации подхода надо создать React компоненты и объявить в window поле для вызова функции рендера:
Через Vite нужно собрать файл reactIslands.js и добавить код в файл шаблонизатора рендера HTML страницы:
Как видите из примера выше, здесь используется jQuery и шаблонизатор Jinja. Начальные пропсы в React передаются с помощью шаблонизатора.
https://swizec.com/blog/the-anatomy-of-a-react-island/
React Island – это подход внедрения React компонентов и даже целых приложений в статическую HTML страницу. Этот подход можно использовать для постепенного рефакторинга статического сайта или, если вы хотите добавить небольшую интерактивную часть на сайт, оставить все остальное как есть статическим.
Как это работает:
1) Рендерите HTML любым способом
2) Добавляете корневой div в HTML для React
3) Вызываете функцию
4) Рендерите небольшое React приложение в добавленный div элемент
React Island подойдет если у вас есть сайт, который рендерится статически через шаблонизатор. Автор предлагает не переписывать все сразу на React, т.к. на это может уйти много времени, а вместо этого делать постепенный рефакторинг. Либо оставить все как есть и добавлять небольшую интерактивность частями.
Для реализации подхода надо создать React компоненты и объявить в window поле для вызова функции рендера:
const ReactIslands = {
amazingNewFeature: function amazingNewFeature(
element: HTMLDivElement,
props: AmazingNewFeatureProps
) {
createRoot(element).render(
<App>
<AmazingNewFeature {...props} />
</App>
);
},
};
window.ReactIslands = ReactIslands;
Через Vite нужно собрать файл reactIslands.js и добавить код в файл шаблонизатора рендера HTML страницы:
<script
type="module"
src="{{ url_for('static', filename='reactIslands.js') }}"
></script>
<script>
$(document).ready(function () {
window.ReactIslands.amazingNewFeature(
document.getElementById('amazing-new-feature'),
{
prop1: {{ value_from_server | tojson }},
prop2: {{ value_from_server | tojson }},
})
})
</script>
<div id="amazing-new-feature"></div>
Как видите из примера выше, здесь используется jQuery и шаблонизатор Jinja. Начальные пропсы в React передаются с помощью шаблонизатора.
https://swizec.com/blog/the-anatomy-of-a-react-island/
Swizec
The anatomy of a React Island | Swizec Teller
A coworker asked how React Islands work and I realized it's a technique I've been using to modernize monolithic web codebases for years, but never wrote down how it works.
👍12🔥4👎1
Релиз React 19
Вышла стабильная версия React 19. Что появилось нового:
⭐️ Actions
В React одним из основных кейсов использования является выполнение мутаций и обновление стейта в ответ. В React 19 добавили поддержку использования асинхронных функций в переходах для автоматической обработки стейта pending, ошибок, форм и оптимистичных обновлений.
По соглашению функции, использующие асинхронные переходы, называются «Actions». Новые хуки, которые относятся к «Actions»:
🔴 useActionState – новый хук для основных кейсов с «Actions». В Canary версии назывался useFormState. Принимает функцию «Action» и возвращает «Action» для композиции.
Возвращаемую функцию можно использовать как action в формах:
🔴 useFormStatus – хук для отслеживания стейта форма. Используется в дочерних компонентах формы. Заменяет проп-дриллинг и контекст. Хук читает стейт родительской формы:
🔴 useOptimistic – хук для оптимистического отображения финального состояния запроса.
🔴 use – хук для чтения ресурсов. Например, можно читать Promise и React вызовет Suspend пока промис не зарезолвится. Также можно читать значение контекста:
⭐️ Новые функции prerender и prerenderToNodeStream для генерации статики. Они предназначены для стриминга ответа через Node.js Streams и Web Streams. Пример:
⭐️ React Server Components. В React 19 включены все фичи серверных компонентов из Canary релиза, также есть Server Actions.
Улучшения в React 19:
⭐️ ref как проп. Больше не надо использовать forwardRef для прокидывания рефа:
⭐️ ref колбек может вернуть функцию очистки, которая вызовется когда элемент будет удален из DOM:
⭐️ Улучшенный отчет по ошибкам, например если произошла ошибка гидратации, то показывается дифф несоответствия.
⭐️ В React 19 можно использовать <Context> как провайдер вместо <Context.Provider>.
⭐️ Поддержка мета-тегов и тега style. Если добавить в компонент мета-теги или тег style, то React самостоятельно расположит их внутри head:
Это будет работать и с серверным рендерингом.
⭐️ Появилась возможность указать браузеру о необходимости предзагрузки ресурсов через функции preload, preinit, prefetchDNS, preconnect. Эти вызовы функции преобразуются в соответствующие теги предзагрузки link в head.
https://react.dev/blog/2024/12/05/react-19
Вышла стабильная версия React 19. Что появилось нового:
В React одним из основных кейсов использования является выполнение мутаций и обновление стейта в ответ. В React 19 добавили поддержку использования асинхронных функций в переходах для автоматической обработки стейта pending, ошибок, форм и оптимистичных обновлений.
По соглашению функции, использующие асинхронные переходы, называются «Actions». Новые хуки, которые относятся к «Actions»:
const [error, submitAction, isPending] = useActionState(
async (previousState, newName) => {
const error = await updateName(newName);
if (error) {
return error;
}
return null;
},
null,
);
Возвращаемую функцию можно использовать как action в формах:
<form action={submitAction}>
import {useFormStatus} from 'react-dom';
function DesignButton() {
const {pending} = useFormStatus();
return <button type="submit" disabled={pending} />
}
import {use} from 'react';
function Comments({commentsPromise}) {
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
import { prerender } from 'react-dom/static';
async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}
Улучшения в React 19:
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
<MyInput ref={ref} />
<input
ref={(ref) => {
return () => {
// очистка
};
}}
/>
function BlogPost({post}) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name="author" content="Josh" />
<link rel="author" href="https://twitter.com/joshcstory/" />
<link rel="stylesheet" href="foo" precedence="default" />
<meta name="keywords" content={post.keywords} />
</article>
);
}
Это будет работать и с серверным рендерингом.
https://react.dev/blog/2024/12/05/react-19
Please open Telegram to view this post
VIEW IN TELEGRAM
👍26🔥9❤5👎3
Biome – быстрый форматтер и линтер
Biome – тулчейн для фронтенда, который умеет форматировать как Prettier, но в 35 раз быстрее. Кроме форматирования, есть производительный линтинг, поддерживающий более 300 правил ESLint, TypeScript ESLint и других. Линтинг в 15 раз быстрее чем ESLint (без плагинов). Можно выполнить форматирование и линтинг одной командой:
Biome можно установить как отдельный бинарник и запускать без Node.js. Это может значительно ускорить проверки в CI.
У Biome свой файл конфигурации для настроек линтинга и форматирования. Есть команда для миграции с Prettier и ESLint, например:
Пример конфига:
https://biomejs.dev/
Biome – тулчейн для фронтенда, который умеет форматировать как Prettier, но в 35 раз быстрее. Кроме форматирования, есть производительный линтинг, поддерживающий более 300 правил ESLint, TypeScript ESLint и других. Линтинг в 15 раз быстрее чем ESLint (без плагинов). Можно выполнить форматирование и линтинг одной командой:
npx @biomejs/biome check --write ./src
Biome можно установить как отдельный бинарник и запускать без Node.js. Это может значительно ускорить проверки в CI.
У Biome свой файл конфигурации для настроек линтинга и форматирования. Есть команда для миграции с Prettier и ESLint, например:
biome migrate eslint –write
biome migrate prettier –write
Пример конфига:
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"css": {
"linter": {
"enabled": true
}
},
"files": {
"ignore": [
"dist/**",
],
"include": [
"src/**"
]
},
"formatter": {
"ignore": [
"configuration_schema.json"
]
},
"json": {
"formatter": {
"indentStyle": "space",
"lineWidth": 1
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"style": {
"noNonNullAssertion": "off"
}
}
},
"organizeImports": {
"enabled": true
}
}
https://biomejs.dev/
Biome
Format, lint, and more in a fraction of a second.
👍27🔥15❤1
SSR — это не дорого
Все чаще можно увидеть сообщения о том, что SSR это дорого и не нужно. В своем блоге Тео Браун заявляет, наоборот, что SSR — это не дорого. О чем он пишет:
🔴 Накладные расходы на SSR минимальны.
Отрендерить JSON в HTML относительно несложная задача, которая чаще всего может занять менее 20 мс. Основное время может уйти на запросы в базу данных и проверку аутентификации. Если данные не защищены аутентификацией, то их можно закэшировать и положить в CDN.
🔴 SSR может сократить другие расходы на облаке
Представьте SPA приложение, в котором при инициализации несколько компонентов делают запросы в API. В каждом запросе происходит создание TCP соединения, проверка аутентификации, парсинг HTTP заголовков, подключение к базе данных. Эти накладные расходы суммируются, как с точки зрения производительности, так и с точки зрения стоимости.
🔴 SSR предоставляет лучший UX.
SSR не только улучшает SEO, но и дает другие преимущества:
- Метаданные для роутов: лучше UX для сохранения в закладки и передачу ссылок в соц. сетях.
- Сплит бандла по роутам: уменьшение размера страницы при загрузке.
- Загрузка страницы с данными, вместо белого экрана.
https://t3.gg/blog/post/ssr-is-not-expensive
Все чаще можно увидеть сообщения о том, что SSR это дорого и не нужно. В своем блоге Тео Браун заявляет, наоборот, что SSR — это не дорого. О чем он пишет:
Отрендерить JSON в HTML относительно несложная задача, которая чаще всего может занять менее 20 мс. Основное время может уйти на запросы в базу данных и проверку аутентификации. Если данные не защищены аутентификацией, то их можно закэшировать и положить в CDN.
Представьте SPA приложение, в котором при инициализации несколько компонентов делают запросы в API. В каждом запросе происходит создание TCP соединения, проверка аутентификации, парсинг HTTP заголовков, подключение к базе данных. Эти накладные расходы суммируются, как с точки зрения производительности, так и с точки зрения стоимости.
SSR не только улучшает SEO, но и дает другие преимущества:
- Метаданные для роутов: лучше UX для сохранения в закладки и передачу ссылок в соц. сетях.
- Сплит бандла по роутам: уменьшение размера страницы при загрузке.
- Загрузка страницы с данными, вместо белого экрана.
https://t3.gg/blog/post/ssr-is-not-expensive
Please open Telegram to view this post
VIEW IN TELEGRAM
t3.gg
Debunking the Myth: SSR Isn't Expensive
SSR is not expensive. It's actually a great way to save money and improve the user experience.
👎23👍3
Как React Compiler работает с реальным кодом
В своем блоге Надя Макаревич изучила влияние React Compiler на начальную загрузку и производительность реального приложения.
В итоге оказалось, что React Compiler как минимум не ухудшает, но чаще улучшает начальную загрузку и производительность приложения. Однако компилятор не обрабатывает все случаи ре-рендера, поэтому если хотите лучшую производительность, то полагаться на работу только React Compiler не получится. Придется некоторые кейсы оптимизировать самостоятельно. В целом React Compiler оптимизирует приложение достаточно хорошо.
Один из примеров, которые потребовал самостоятельной оптимизации. На странице рендерился список карточек:
Объект data приходил из запроса React Query, а проп preview был объектом. Когда в запросе React Query изменялся набор параметров, то возвращался другой объект data. Даже если объекты карточек внутри массива одинаковые, то все равно происходит ре-рендер компонента GalleryCardMemo из-за того, что проп preview объект. Чтобы решить проблему, надо проп preview разделить на несколько пропов-примитивов. Например:
https://www.developerway.com/posts/how-react-compiler-performs-on-real-code
В своем блоге Надя Макаревич изучила влияние React Compiler на начальную загрузку и производительность реального приложения.
В итоге оказалось, что React Compiler как минимум не ухудшает, но чаще улучшает начальную загрузку и производительность приложения. Однако компилятор не обрабатывает все случаи ре-рендера, поэтому если хотите лучшую производительность, то полагаться на работу только React Compiler не получится. Придется некоторые кейсы оптимизировать самостоятельно. В целом React Compiler оптимизирует приложение достаточно хорошо.
Один из примеров, которые потребовал самостоятельной оптимизации. На странице рендерился список карточек:
// somewhere before
const GalleryCardMemo = React.iss.onemo(GalleryCardMemo);
// somewhere in render function
{data?.data?.map((example) => {
return (
<GalleryCardMemo
href={`/examples/code-examples/${example.key}`}
key={example.key}
title={example.name}
preview={example.previewUrl}
/>
);
})}
Объект data приходил из запроса React Query, а проп preview был объектом. Когда в запросе React Query изменялся набор параметров, то возвращался другой объект data. Даже если объекты карточек внутри массива одинаковые, то все равно происходит ре-рендер компонента GalleryCardMemo из-за того, что проп preview объект. Чтобы решить проблему, надо проп preview разделить на несколько пропов-примитивов. Например:
{data?.data?.map((example) => {
return (
<GalleryCardMemo
href={`/examples/code-examples/${example.key}`}
key={example.key}
title={example.name}
// pass primitives values instead of the entire object
previewLight={example.previewUrl.light}
previewDark={example.previewUrl.dark}
/>
);
})}
https://www.developerway.com/posts/how-react-compiler-performs-on-real-code
Developerway
How React Compiler Performs on Real Code
Exploring the impact of React Compiler on initial load and interaction performance. With numbers. Measured on a real app.
👍9👎1
Base UI
Вышла альфа-версия UI библиотеки Base UI от авторов Radix, MUI и Floating UI. Библиотека представляет из себя набор нестилизованных React компонентов для создания доступных интерфейсов. В библиотеке уделено особое внимание доступности, производительности и удобству разработчика.
Каждый компонент собирается из частей. Пример компонента Popover:
Для стилизации можно использовать что угодно, здесь для примера используется CSS Modules.
https://base-ui.com/react/overview/quick-start
Вышла альфа-версия UI библиотеки Base UI от авторов Radix, MUI и Floating UI. Библиотека представляет из себя набор нестилизованных React компонентов для создания доступных интерфейсов. В библиотеке уделено особое внимание доступности, производительности и удобству разработчика.
Каждый компонент собирается из частей. Пример компонента Popover:
import * as React from 'react';
import { Popover } from '@base-ui-components/react/popover';
import styles from './index.module.css';
export default function ExamplePopover() {
return (
<Popover.Root>
<Popover.Trigger className={styles.IconButton}>
<BellIcon aria-label="Notifications" className={styles.Icon} />
</Popover.Trigger>
<Popover.Portal>
<Popover.Positioner sideOffset={8}>
<Popover.Popup className={styles.Popup}>
<Popover.Arrow className={styles.Arrow}>
<ArrowSvg />
</Popover.Arrow>
<Popover.Title className={styles.Title}>Notifications</Popover.Title>
<Popover.Description className={styles.Description}>
You are all caught up. Good job!
</Popover.Description>
</Popover.Popup>
</Popover.Positioner>
</Popover.Portal>
</Popover.Root>
);
}
Для стилизации можно использовать что угодно, здесь для примера используется CSS Modules.
https://base-ui.com/react/overview/quick-start
Base-Ui
Base UI
A quick guide to getting started with Base UI.
👍17
Вам не нужен Next.js
Автор мигрировал админку с Next.js на обычный React и TanStack Router с Rspack и сократил время сборки проекта с 3 минут до 18 секунд, а Hot reload стал меньше 200мс.
По началу автору нравилось использование Next.js и все было хорошо, пока не начались неприятности:
🔴 Высокая стоимость за использование Serverless функций.
🔴 Сложное тестирование API из-за того серверные действия были смешаны с API Routes.
🔴 Постепенное увеличение времени сборки, который доходил до 7 минут.
🔴 Утяжеление локальной разработки – каждое малейшее изменение приводило к полной перезагрузке SSR.
Каждое техническое решение требует компромиссов. Что потеряли при уходе с Next.js:
🔴 Совместная разработка фронтенда и бэкенда. Теперь приходится разделять код фронта и бэкенда.
🔴 Встроенная Next.js фича кэширования запросов.
🔴 Пре-рендер и быстрая первичная загрузка страница. Нет автоматической генерации статики. Теперь требуется более продуманное разделение кода для сохранения скорости загрузки страницы.
🔴 Серверные компоненты и действия. Это удобный для разработки подход, с помощью которого можно быстро организовать API.
https://www.comfydeploy.com/blog/you-dont-need-nextjs
Автор мигрировал админку с Next.js на обычный React и TanStack Router с Rspack и сократил время сборки проекта с 3 минут до 18 секунд, а Hot reload стал меньше 200мс.
По началу автору нравилось использование Next.js и все было хорошо, пока не начались неприятности:
Каждое техническое решение требует компромиссов. Что потеряли при уходе с Next.js:
https://www.comfydeploy.com/blog/you-dont-need-nextjs
Please open Telegram to view this post
VIEW IN TELEGRAM
Comfydeploy
You don't need Next JS - Comfy Deploy
The best platform to run and deploy ComfyUI to your teams
👍23👎6❤2
В React добавили компонент <ViewTransition>
Появился PR с добавлением View Transition API в виде компонента <ViewTransition>. View Transition API предназначен для создания анимации плавного перехода между страницами или состояниями приложения.
В React <ViewTransition> будет активироваться только для асинхронных обновлений, таких как startTransition, useDeferredValue, Actions или <Suspense>. Концептуально компонент <ViewTransition> похож на React Fragment, который делает переход для дочерних элементов. Пример:
Новый компонент умеет работать с Suspense. Пример который триггерит exit у Skeleton и enter у Content:
Новое API будет работать не только в вебе, но и в React Native.
https://github.com/facebook/react/pull/31975
Появился PR с добавлением View Transition API в виде компонента <ViewTransition>. View Transition API предназначен для создания анимации плавного перехода между страницами или состояниями приложения.
В React <ViewTransition> будет активироваться только для асинхронных обновлений, таких как startTransition, useDeferredValue, Actions или <Suspense>. Концептуально компонент <ViewTransition> похож на React Fragment, который делает переход для дочерних элементов. Пример:
<ViewTransition>{condition ? <ComponentA /> : <ComponentB />}</ViewTransition>
Новый компонент умеет работать с Suspense. Пример который триггерит exit у Skeleton и enter у Content:
<Suspense fallback={<ViewTransition><Skeleton /></ViewTransition>}>
<ViewTransition><Content /></ViewTransition>
</Suspense>
Новое API будет работать не только в вебе, но и в React Native.
https://github.com/facebook/react/pull/31975
GitHub
Add `<ViewTransition>` Component by sebmarkbage · Pull Request #31975 · facebook/react
This will provide the opt-in for using View Transitions in React.
View Transitions only trigger for async updates like startTransition, useDeferredValue, Actions or <Suspense> reveali...
View Transitions only trigger for async updates like startTransition, useDeferredValue, Actions or <Suspense> reveali...
👍31🔥4
Как бандлеры работают с React Server Components
Мейнтенер RSPack написал подробную статью о том, как бандлеры Webpack и Turbopack работают с React Server Components и серверными действиями. Хоть и для RSC есть описание функционала в документе RFC, но информация об интеграциях (с бандлерами, с роутером и т.д.) в нем описана относительно просто. Поэтому автор решил написал статью о том, как происходит интеграция RSC с бандлерами на примере Webpack и Turbopack.
⭐️ Как устроен бандлинг клиентского компонента в серверных компонентах
В процессе бандлинга клиентского компонента "use client" в окружение сервера заменится ссылкой на компонент:
Будет заменен на:
Когда сервер рендерит серверный компонент, импортированный клиентский компонент будет являться фактически ссылкой на клиентский компонент.
Сериализованный JSX кода выше:
При отправке клиенту (включая SSR) для рендеринга результата работы серверного компонента (сериализованный JSX), при встрече с клиентской ссылкой, клиентский модуль с компонентом будет загружен через эти метаданные. Сначала React загрузит чанки по
https://github.com/orgs/web-infra-dev/discussions/23
Мейнтенер RSPack написал подробную статью о том, как бандлеры Webpack и Turbopack работают с React Server Components и серверными действиями. Хоть и для RSC есть описание функционала в документе RFC, но информация об интеграциях (с бандлерами, с роутером и т.д.) в нем описана относительно просто. Поэтому автор решил написал статью о том, как происходит интеграция RSC с бандлерами на примере Webpack и Turbopack.
В процессе бандлинга клиентского компонента "use client" в окружение сервера заменится ссылкой на компонент:
// src/ClientComp.js
"use client"
export function ClientComp() { return <div>...</div> }
Будет заменен на:
import { createClientReference } from "plugin/runtime/server.js"
export let ClientComp = createClientReference("src/ClientComp.js#ClientComp")
Когда сервер рендерит серверный компонент, импортированный клиентский компонент будет являться фактически ссылкой на клиентский компонент.
import { ClientComp } from "./ClientComp"
export async function App() { return <div><ClientComp /></div> }
Сериализованный JSX кода выше:
0:"$L1"
// Результат рендеринга Client Reference, включая некоторые метаданные
2:I{"id":"./src/ClientComp.js","chunks":["client0"],"name":"ClientComp","async":false}
1:["$","div",null,{"children":["$","$L2",null,{}]}]
При отправке клиенту (включая SSR) для рендеринга результата работы серверного компонента (сериализованный JSX), при встрече с клиентской ссылкой, клиентский модуль с компонентом будет загружен через эти метаданные. Сначала React загрузит чанки по
__webpack_chunk_load__(chunkId), затем запустит соответствующий модуль по __webpack_require__(moduleId), и, наконец, получит экспортированный клиентский компонент для рендеринга.https://github.com/orgs/web-infra-dev/discussions/23
Please open Telegram to view this post
VIEW IN TELEGRAM
GitHub
RSC and Server Action bundle practice · web-infra-dev · Discussion #23
This article introduces the bundle practices of RSC (React Server Components) and Server Action in React, including their concepts, how it rendered, how it bundled in webpack, and how Turbopack bun...
👍16🔥4❤1
Основы доступности, которые должен знать каждый фронтенд-разработчик
Мартин Холс в своем блоге рассказал о ключевых принципах доступности, которые должен знать каждый фронтенд-разработчик при разработке компонентов. Часть того, о чем пишет автор:
🔴 Семантический HTML. Используйте правильные элементы для интерактивности и нативные элементы. Если нужно показать кнопку, то используйте <button>, а для ссылок <a>. Не надо вместо этого использовать onClick на div элементе. Такие нативные элементы как <select>, <input> и <textarea> доступны из коробки, старайтесь использовать их. Но если запланируете делать кастомный элемент на подобие <select>, то лучше использовать готовые решения, например, react-select.
🔴 Формы. Каждое поле формы должно быть внутри элемента <form> с onSubmit и кнопкой отправки. Это позволяет браузерам определять связанные поля формы и, например, на мобильных устройствах позволяет переключаться между полями без закрытия клавиатуры.
🔴 Навигация с помощью клавиатуры. Проверьте что в приложении можно переключаться между элементами по нажатию Tab и работает Enter. Используйте CSS индикаторы фокуса :focus-visible и :focus.
🔴 Модалки. Сделать их доступными может быть не просто. Важно при открытии модалки переключать фокус внутри него, а при закрытии возвращать фокус на кнопку открытия модалки.
🔴 Стилизация. Улучшайте доступность интерфейсов через стилизацию, например:
- проверьте, что ссылки выглядят как ссылки, а кнопки как кнопки;
- сделайте отчетливые состояния элемента при наведении, активном и выключенном состоянии;
- используйте достаточный контраст цветов для различения элементов;
- поддержите пользовательские размеры шрифтов и зуминг;
- поддержите настройку отключения анимации;
https://martijnhols.nl/blog/accessibility-essentials-every-front-end-developer-should-know
Мартин Холс в своем блоге рассказал о ключевых принципах доступности, которые должен знать каждый фронтенд-разработчик при разработке компонентов. Часть того, о чем пишет автор:
- проверьте, что ссылки выглядят как ссылки, а кнопки как кнопки;
- сделайте отчетливые состояния элемента при наведении, активном и выключенном состоянии;
- используйте достаточный контраст цветов для различения элементов;
- поддержите пользовательские размеры шрифтов и зуминг;
- поддержите настройку отключения анимации;
https://martijnhols.nl/blog/accessibility-essentials-every-front-end-developer-should-know
Please open Telegram to view this post
VIEW IN TELEGRAM
Martijn Hols
Accessibility essentials every front-end developer should know by Martijn Hols
Essential accessibility practices for front-end developers, including semantic HTML, alt texts, ARIA, and keyboard navigation tips to build inclusive components.
🔥12👍8
Делаем использование FormData типобезопасным
Новый хук useActionState в React 19 позволяет обрабатывать отправку данных в формах, пример:
При отправке формы в колбек хука приходит объект типа FormData. В примере formData.get может принимать любой string и нет уверенности что этот string совпадает с именем поля на форме. Для решения этой проблемы, автор предлагает создать компонент поля, которое принимает дженерик с названиями полей и создать функцию получения значения из FormData, которое также принимает дженерик. Пример:
Теперь тип FormName связывает поля на форме и FormData, так что больше нет риска опечаток.
https://www.typeonce.dev/article/make-form-data-and-input-names-type-safe-in-react
Новый хук useActionState в React 19 позволяет обрабатывать отправку данных в формах, пример:
export default function Page() {
const [_, action, pending] = useActionState((_: unknown, formData: FormData) => {
const firstName = formData.get("firstName");
const age = formData.get("age");
}, null);
return (
<form action={action}>
<input type="text" name="firstName" />
<input type="number" name="age" />
<button type="submit" disabled={pending}>Submit</button>
</form>
);
}
При отправке формы в колбек хука приходит объект типа FormData. В примере formData.get может принимать любой string и нет уверенности что этот string совпадает с именем поля на форме. Для решения этой проблемы, автор предлагает создать компонент поля, которое принимает дженерик с названиями полей и создать функцию получения значения из FormData, которое также принимает дженерик. Пример:
type FormName = "firstName" | "age";
const get = <Name extends string = never>(
formData: FormData,
name: NoInfer<Name>
) => formData.get(name);
export default function Page() {
const [_, action] = useActionState((_: unknown, formData: FormData) => {
const firstName = get<FormName>(formData, "firstName");
const age = get<FormName>(formData, "age");
}, null);
return (
<form action={action}>
<SaveInput<FormName> type="text" name="firstName" />
<SaveInput<FormName> type="number" name="age" />
<button type="submit">Submit</button>
</form>
);
}
Теперь тип FormName связывает поля на форме и FormData, так что больше нет риска опечаток.
https://www.typeonce.dev/article/make-form-data-and-input-names-type-safe-in-react
typeonce.dev
Make FormData and input names type-safe in React | Typeonce
Professional training for Full-Stack software development teams
👎12👍3❤1
This media is not supported in your browser
VIEW IN TELEGRAM
react-scan для поиска проблем производительности
Вышла библиотека react-scan для поиска проблем производительности и устранения медленного рендера приложения.
В отличии от других похожих библиотек, для установки react-scan не требуется изменений в коде приложения, достаточно вставить тег <script> или можно установить как модуль. Библиотека react-scan выделяет только те компоненты, которые надо оптимизировать.
Разработчики обещают в будущем выпустить расширение для браузера.
https://github.com/aidenybai/react-scan
Вышла библиотека react-scan для поиска проблем производительности и устранения медленного рендера приложения.
В отличии от других похожих библиотек, для установки react-scan не требуется изменений в коде приложения, достаточно вставить тег <script> или можно установить как модуль. Библиотека react-scan выделяет только те компоненты, которые надо оптимизировать.
Разработчики обещают в будущем выпустить расширение для браузера.
https://github.com/aidenybai/react-scan
👍38❤1
UX++ и DX++ с движками синхронизации
В своем блоге Карл Ассманн объясняет почему сложно инвалидировать запросы и реализовать оптимистичный UI с React Query.
Сложность в инвалидации запросов заключается в том, что надо знать какие запросы нужно инвалидировать и когда. Если инвалидировать лишние ключи, то будут выполняться лишние запросы в сети. Также нужно знать все ключи, которые надо инвалидировать, иначе часть UI будет продолжать показывать устаревшие данные.
Для эффективной реализации оптимистичного UI нужно знать все связанные ключи запроса и влияние каждой мутации на них. Пример реализации оптимистичного UI:
В оптимистичном UI также возникают сложности при добавлении нового запроса. Нужно связать существующие мутации, убедиться, что они инвалидируют этот запрос или вставляют правильные данные для оптимистичного UI.
По мнению автора, основная проблема клиентских приложений в том, что они работают с состоянием на сервере. Бывает нужно выполнить слишком много запросов, из-за которых пользователям приходится ждать, прежде чем они смогут продолжить.
В качестве решения проблемы автор предлагает обратить внимание на движки синхронизации, например Replicache.
Основная идея заключается в том, чтобы иметь локальную копию данных на клиенте. Компоненты подписываются на локальные данные или изменяют их, в то время как движок синхронизирует изменения с сервером в фоновом режиме.
Replicache работает только на клиенте, предоставляет методы подписки и мутации и взаимодействует с конечными точками API, позволяя вам контролировать ваш сервер, вашу базу данных и вашу бизнес-логику.
Пример подписки:
https://www.carlassmann.com/blog/improve-ux-dx-with-sync-engines
В своем блоге Карл Ассманн объясняет почему сложно инвалидировать запросы и реализовать оптимистичный UI с React Query.
Сложность в инвалидации запросов заключается в том, что надо знать какие запросы нужно инвалидировать и когда. Если инвалидировать лишние ключи, то будут выполняться лишние запросы в сети. Также нужно знать все ключи, которые надо инвалидировать, иначе часть UI будет продолжать показывать устаревшие данные.
Для эффективной реализации оптимистичного UI нужно знать все связанные ключи запроса и влияние каждой мутации на них. Пример реализации оптимистичного UI:
const mutation = useMutation(addTodo, {
onMutate: async (newTodo) => {
const previousTodos = queryClient.getQueryData(...);
queryClient.setQueryData([newTodo, ...previousTodos]);
return { previousTodos };
},
onError: (_, _, context) => {
queryClient.setQueryData(["todos"], context.previousTodos);
},
onSettled: () => {
queryClient.invalidateQueries(["todos"]);
},
});
В оптимистичном UI также возникают сложности при добавлении нового запроса. Нужно связать существующие мутации, убедиться, что они инвалидируют этот запрос или вставляют правильные данные для оптимистичного UI.
По мнению автора, основная проблема клиентских приложений в том, что они работают с состоянием на сервере. Бывает нужно выполнить слишком много запросов, из-за которых пользователям приходится ждать, прежде чем они смогут продолжить.
В качестве решения проблемы автор предлагает обратить внимание на движки синхронизации, например Replicache.
Основная идея заключается в том, чтобы иметь локальную копию данных на клиенте. Компоненты подписываются на локальные данные или изменяют их, в то время как движок синхронизирует изменения с сервером в фоновом режиме.
Replicache работает только на клиенте, предоставляет методы подписки и мутации и взаимодействует с конечными точками API, позволяя вам контролировать ваш сервер, вашу базу данных и вашу бизнес-логику.
Пример подписки:
function Todos() {
const todos = useSubscribe(rep, async (tx) => {
return await tx.scan({prefix: "todo/"}).toArray();
});
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>✅ {todo.title}</li>
))}
</ul>
);
}
https://www.carlassmann.com/blog/improve-ux-dx-with-sync-engines
Carlassmann
UX++ and DX++ with Sync Engines
I share what I learn, do and think
👍14
Create React App устарел
Create React App был одним из основных инструментов для старта приложения в 2017-2021 году. Сейчас он официально устарел и в Readme рекомендуют перейти на другой React фреймворк.
Если вы захотите использовать CRA с React 19, то столкнетесь с множеством NPM ошибок при настройке проекта. Это обусловлено сочетанием нескольких факторов. CRA всегда пытается установить последнюю версию React по умолчанию, но 19 версии не указана в шаблонах пакета. Шаблон по умолчанию использует
https://github.com/facebook/create-react-app/issues/17004
Create React App был одним из основных инструментов для старта приложения в 2017-2021 году. Сейчас он официально устарел и в Readme рекомендуют перейти на другой React фреймворк.
Если вы захотите использовать CRA с React 19, то столкнетесь с множеством NPM ошибок при настройке проекта. Это обусловлено сочетанием нескольких факторов. CRA всегда пытается установить последнюю версию React по умолчанию, но 19 версии не указана в шаблонах пакета. Шаблон по умолчанию использует
@testing-library/[email protected], который зависит от react@18. Это значит, что установка 19 версии приведет к несоответствию версий. https://github.com/facebook/create-react-app/issues/17004
GitHub
Umbrella: CRA breaks with React 19, and CRA needs deprecation notices · Issue #17004 · facebook/create-react-app
Per request from @rickhanlonii : Background / Primary Problem Starting with the release of React 19, users running create-react-app my-app began experiencing hard errors from NPM during project set...
👍12❤6🔥2
Почему я не буду использовать JSDOM
Артем Захарченко предлагает перестать пользоваться библиотекой JSDOM при написании тестов компонентов. JSDOM эмулирует браузер в Node.js, но со своими особенностями. Например, событие Event использует класс dom.window.Event из JSDOM. Это событие не является браузерным или даже Node.js событием. Поэтому произойдет ошибка, если это событие будет обработано где-то еще, кроме JSDOM, например:
Для реализации API браузера JSDOM использует полифиллы, даже для таких Node.js API как fetch, Event, MessageChannel. Однако JSDOM не полностью поддерживает весь функционал браузера, например не поддерживает structuredClone.
По мнению автора статьи, для прогона тестов лучше использовать Vitest Browser Mode и Playwright, которые запускают тесты в настоящем браузере.
https://www.epicweb.dev/why-i-won-t-use-jsdom
Артем Захарченко предлагает перестать пользоваться библиотекой JSDOM при написании тестов компонентов. JSDOM эмулирует браузер в Node.js, но со своими особенностями. Например, событие Event использует класс dom.window.Event из JSDOM. Это событие не является браузерным или даже Node.js событием. Поэтому произойдет ошибка, если это событие будет обработано где-то еще, кроме JSDOM, например:
import { JSDOM } from 'jsdom'
const dom = new JSDOM()
const clickEvent = new dom.window.Event('click')
console.log(clickEvent instanceof globalThis.Event)
// false
const target = new EventTarget()
target.dispatchEvent(clickEvent) // Выкинет исключение
Для реализации API браузера JSDOM использует полифиллы, даже для таких Node.js API как fetch, Event, MessageChannel. Однако JSDOM не полностью поддерживает весь функционал браузера, например не поддерживает structuredClone.
По мнению автора статьи, для прогона тестов лучше использовать Vitest Browser Mode и Playwright, которые запускают тесты в настоящем браузере.
https://www.epicweb.dev/why-i-won-t-use-jsdom
Epic Web Dev
Why I Won’t Use JSDOM
Explore how JSDOM's browser simulation works, and learn front-end testing approaches using Vitest Browser Mode for direct browser testing and native APIs
👍9❤1
Сохранение данных форм с помощью Nuqs
Во время заполнения формы может потребоваться промежуточное сохранение данных, чтобы при обновлении страницы пользователь не потерял введенные данные, либо поделиться данными формой с другими пользователями.
Вариант с localStorage не самый подходящий, с его помощью нельзя поделиться ссылкой на форму с другими пользователями. Автор статьи предлагает использовать библиотеку Nuqs, с помощью которой можно сохранять данные формы в URL в query params. Его можно легко интегрировать с Zod и useForm от react-hook-form и у него встроенная поддержка типизации. Пример формы:
https://armand-salle.fr/post/persisting-form-data-in-react-a-modern-approach-with-nuqs/
Во время заполнения формы может потребоваться промежуточное сохранение данных, чтобы при обновлении страницы пользователь не потерял введенные данные, либо поделиться данными формой с другими пользователями.
Вариант с localStorage не самый подходящий, с его помощью нельзя поделиться ссылкой на форму с другими пользователями. Автор статьи предлагает использовать библиотеку Nuqs, с помощью которой можно сохранять данные формы в URL в query params. Его можно легко интегрировать с Zod и useForm от react-hook-form и у него встроенная поддержка типизации. Пример формы:
import { useQueryState, parseAsJson } from "nuqs"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"
// Define our schema
const statusEnum = ["working", "chilling", "cooking"] as const
const formSchema = z.object({
firstName: z.string(),
lastName: z.string(),
status: z.enum(statusEnum),
})
type FormSchemaType = z.infer<typeof formSchema>
function MyForm() {
// Initialize Nuqs state
const [jsonData, setJsonData] = useQueryState(
"json",
parseAsJson(formSchema.parse),
)
// Set up form with React Hook Form
const form = useForm<FormSchemaType>({
resolver: zodResolver(formSchema),
values: jsonData, // Pre-fill form with URL data
})
const handleSubmit = form.handleSubmit((data) => {
console.log(data)
setJsonData(data) // Update URL state
})
return (
<form onSubmit={handleSubmit}>
<TextInput label="First name" {...form.register("firstName")} />
<TextInput label="Last name" {...form.register("lastName")} />
<SelectInput
label="Status"
options={statusEnum}
{...form.register("status")}
/>
<Button type="submit">Submit</Button>
</form>
)
}
https://armand-salle.fr/post/persisting-form-data-in-react-a-modern-approach-with-nuqs/
armand-salle.fr
Persisting Form Data in React: A Modern Approach with Nuqs
Discover how to persist and share form data in React using Nuqs, a modern type-safe URL state manager. Move beyond localStorage limitations to create shareable, type-safe forms with zero backend requirements.
👍15❤2👎1
State of React 2024
Вышла статистика по React за 2024. В ней собрана информация о наиболее/наименее популярных фичах в React, библиотеках, инструментах. Вот наиболее интересные факты:
⭐️ forwardRef — самый нелюбимый API в React, который, кстати, в 19 версии стал устаревшим и можно прокидывать ref напрямую в пропсы.
⭐️ React Compiler и React Server Components наиболее популярные из новых фич React, при этом RSC наиболее сложная и непонятная.
⭐️ Taint API – самый неизвестный API, но это и понятно, т.к. он является экспериментальным.
⭐️ Zod – наиболее популярная библиотека для валидации, а Jest – для тестирования.
⭐️ Next.js, TanStack Query, Redux, Axios – наиболее популярные библиотеки, а TanStack Query и Zustand – самые любимые.
https://2024.stateofreact.com/ru-RU/
Вышла статистика по React за 2024. В ней собрана информация о наиболее/наименее популярных фичах в React, библиотеках, инструментах. Вот наиболее интересные факты:
https://2024.stateofreact.com/ru-RU/
Please open Telegram to view this post
VIEW IN TELEGRAM
Stateofreact
State of React 2024
The 2024 edition of the annual survey about the latest trends in the React ecosystem.
👍24❤6👎4
API captureOwnerStack
Недавно вышедший экспериментальный API React теперь официально задокументирован и без флага. Этот API особенно полезно библиотекам и фреймворкам, поскольку позволяет дополнить вывод ошибки пользователю.
API доступно только в development режиме и считывает текущий Owner Stack и возвращает его в виде строки. Пример:
https://react.dev/reference/react/captureOwnerStack
Недавно вышедший экспериментальный API React теперь официально задокументирован и без флага. Этот API особенно полезно библиотекам и фреймворкам, поскольку позволяет дополнить вывод ошибки пользователю.
API доступно только в development режиме и считывает текущий Owner Stack и возвращает его в виде строки. Пример:
import * as React from 'react';
function Component() {
if (process.env.NODE_ENV !== 'production') {
const ownerStack = React.captureOwnerStack();
console.log(ownerStack);
}
}
https://react.dev/reference/react/captureOwnerStack
react.dev
captureOwnerStack – React
The library for web and native user interfaces
👍6❤1
TypeScript перепишут на Go
Новую версию TypeScript перепишут на Go. Это связано в первую очередь со скоростью работы. TypeScript медленно работает на больших кодовых базах. Особенно это замечается при рефакторинге кода в VS Code, например когда переименовываешь переменную или ищешь все ссылки на интерфейс.
По заверениям Андерса Хейлсберга, при использовании компилятора на Go, скорость работы команды tsc увеличивается в 10 раз. Например, запуск tsc на кодовой базе Playwright вместо 11 секунд занимает 1.1 секунду. Также использование компилятора на Go значительно ускоряет работу редактора VS Code. По замерам автора, скорость загрузки проекта выросла в 8 раз.
Текущая версия TypeScript 5.8. Следующая мажорная версия TypeScript 6 останется написана на TS, но появятся пометки deprecated и критические изменения для поддержки 7ой версии.
Когда в кодовой базе на Go станет достаточно функционала, который покрывает версию на TS, то выпустят TypeScript 7 на Go.
https://devblogs.microsoft.com/typescript/typescript-native-port/
Новую версию TypeScript перепишут на Go. Это связано в первую очередь со скоростью работы. TypeScript медленно работает на больших кодовых базах. Особенно это замечается при рефакторинге кода в VS Code, например когда переименовываешь переменную или ищешь все ссылки на интерфейс.
По заверениям Андерса Хейлсберга, при использовании компилятора на Go, скорость работы команды tsc увеличивается в 10 раз. Например, запуск tsc на кодовой базе Playwright вместо 11 секунд занимает 1.1 секунду. Также использование компилятора на Go значительно ускоряет работу редактора VS Code. По замерам автора, скорость загрузки проекта выросла в 8 раз.
Текущая версия TypeScript 5.8. Следующая мажорная версия TypeScript 6 останется написана на TS, но появятся пометки deprecated и критические изменения для поддержки 7ой версии.
Когда в кодовой базе на Go станет достаточно функционала, который покрывает версию на TS, то выпустят TypeScript 7 на Go.
https://devblogs.microsoft.com/typescript/typescript-native-port/
Microsoft News
A 10x Faster TypeScript
Embarking on a native port of the existing TypeScript compiler and toolset to achieve a 10x performance speed-up.
👍32🔥7👎3
Кейсы оптимизации React
Обзор примеров, показывающих, как пять разных команд разработчиков оптимизировали React. Например, у одной из команд на проекте был плохой показатель метрики INP, которая отслеживает скорость отклика сайта. Хорошим считается отклик менее 200мс, на проекте был отклик 380мс. Какие были найдены причины:
- Большой бандл. Первоначально грузился большой бандл с кодом, который не был нужен. Помог code splitting.
- Определенные колбеки у обработчиков событий вызывались слишком часто, из-за чего блокировался основной поток браузера. Помог debounce на скролл и resize, а также делегирование событий.
- Ре-рендерелись больше компонентов чем нужно при изменении стейта. Помогло оборачивание компонентов в
- Долгий рендеринг компонентов. Помогло обновление до React 18. Основным преимуществом этого обновления стал параллельный рендеринг, поскольку высокоприоритетные задачи, такие как пользовательский клик, могут прерывать рендеринг. Это обновление улучшило метрику INP на 46% на десктопе.
Помимо это помогли следующие исправления для улучшения производительности рендеринга:
- Разбиение сложных хуков на более мелкие. На графике перфоманса было видно, что сложные хуки вызываются несколько раз и перерисовывают компонент без необходимости.
- Замена React Router на window.location там, где это возможно.
- Улучшение мемоизации Redux селекторов.
https://largeapps.dev/case-studies/advanced/
Обзор примеров, показывающих, как пять разных команд разработчиков оптимизировали React. Например, у одной из команд на проекте был плохой показатель метрики INP, которая отслеживает скорость отклика сайта. Хорошим считается отклик менее 200мс, на проекте был отклик 380мс. Какие были найдены причины:
- Большой бандл. Первоначально грузился большой бандл с кодом, который не был нужен. Помог code splitting.
- Определенные колбеки у обработчиков событий вызывались слишком часто, из-за чего блокировался основной поток браузера. Помог debounce на скролл и resize, а также делегирование событий.
- Ре-рендерелись больше компонентов чем нужно при изменении стейта. Помогло оборачивание компонентов в
React.iss.onemo() и использование useMemo для дорогих вычислений.- Долгий рендеринг компонентов. Помогло обновление до React 18. Основным преимуществом этого обновления стал параллельный рендеринг, поскольку высокоприоритетные задачи, такие как пользовательский клик, могут прерывать рендеринг. Это обновление улучшило метрику INP на 46% на десктопе.
Помимо это помогли следующие исправления для улучшения производительности рендеринга:
- Разбиение сложных хуков на более мелкие. На графике перфоманса было видно, что сложные хуки вызываются несколько раз и перерисовывают компонент без необходимости.
- Замена React Router на window.location там, где это возможно.
- Улучшение мемоизации Redux селекторов.
https://largeapps.dev/case-studies/advanced/
👍20