State Management
SWR
Документация Next.js на vercel.com грузится мгновенно, потому что страницы показывают кэш сразу, а свежесть досверяют в фоне. За этим стоит SWR - библиотека от той же Vercel, и её название это и есть стратегия: stale-while-revalidate. Отдай устаревшее, перепроверь следом. SWR делает примерно то же, что TanStack Query, но меньшим набором понятий: один хук useSWR, ключ и функция-фетчер. Выбор между двумя инструментами это не вопрос моды, а баланс объёма и потребностей проекта.
- Сама Vercel и документация Next.js: страницы и дашборды на useSWR, отзывчивость за счёт мгновенного кэша
- Next.js-проекты, где SWR идёт почти как штатный выбор от того же вендора
- Лёгкие интерфейсы и виджеты: дашборд, профиль, счётчики, где хватает чтения с фоновой сверкой
- Команды, которым нужен минимум API и вес поменьше, без мутаций-движка и devtools уровня Query
- GitHub по докладам инженеров применяет stale-while-revalidate как принцип - тот же, что вынесен в имя SWR
Предварительные знания
- Разделение серверного и клиентского состояния и идея кэша с TTL
- Стратегия stale-while-revalidate: показать кэш, сверить в фоне
- Загрузка данных через fetch и промисы
Откуда взялось название stale-while-revalidate
Термин stale-while-revalidate пришёл не из фронтенда, а из HTTP: директива Cache-Control с таким именем описана в RFC 5861 (2010). Смысл прокси и браузерного кэша - отдать клиенту устаревший ответ немедленно, а свежий подтянуть в фоне. В 2019 году Vercel (тогда ZEIT) выпустила библиотеку SWR, перенеся ровно эту стратегию в React-хук для работы с данными. Имя библиотеки буквально и есть имя HTTP-директивы. Идея оказалась настолько удачной, что её под разными названиями переняли все основные инструменты серверного состояния.
Модель stale-while-revalidate в SWR
SWR сводит работу с серверными данными к одному хуку useSWR. Ему дают ключ (обычно URL) и функцию-фетчер, которая по этому ключу вернёт промис. Дальше SWR следует стратегии из своего имени: если по ключу есть кэш, он отдаётся немедленно, даже устаревший, а в фоне запускается сверка с сервером. Пришёл свежий ответ - кэш и интерфейс обновляются. Пользователь почти никогда не видит пустого спиннера, если данные уже были загружены раньше.
Ключ в SWR играет ту же роль, что queryKey в Query: он идентифицирует кэш и дедуплицирует запросы. Два компонента с ключом '/api/profile' дают один сетевой запрос и общий кэш. Фоновая сверка по умолчанию запускается при возврате фокуса на вкладку и при переподключении сети - те же триггеры, что у других инструментов, выросших из той же идеи.
- useSWR('/api/profile', fetcher) - кэша нет, идёт первая загрузка, isLoading равен true
- Данные пришли и закэшированы по ключу '/api/profile'
- Повторный заход на экран: кэш отдаётся мгновенно, в фоне идёт сверка
- Свежий ответ отличается - кэш и все подписчики ключа обновляются
Имя библиотеки это и есть её главное обещание. stale (устаревшее) показывается, while (в то время как) revalidate (идёт сверка). Поэтому интерфейс ощущается мгновенным: данные на экране есть всегда, когда они хоть раз были загружены, а корректность догоняет фоном.
Что означает стратегия stale-while-revalidate, вынесенная в имя SWR?
SWR против TanStack Query
Оба инструмента решают одну задачу - ведут серверное состояние как кэш со свежестью - и оба выросли из идеи stale-while-revalidate. Разница в объёме и философии. SWR намеренно минималистичен: ядро сосредоточено на чтении, API маленький, вес небольшой. TanStack Query шире: развитый движок мутаций с оптимистичными обновлениями, бесконечные и зависимые запросы, persist-слой и собственные devtools - всё в комплекте.
| Свойство | SWR | TanStack Query |
|---|---|---|
| Вендор | Vercel | TanStack (Таннер Линсли) |
| Фокус | Чтение, минимализм | Чтение и запись, полный набор |
| Мутации | Базовые, через mutate | Развитый useMutation с колбэками |
| Инфинит и пагинация | useSWRInfinite | useInfiniteQuery из коробки |
| Devtools | Скромные | Полноценные devtools |
| Фреймворки | React, есть порты | React, Vue, Solid, Svelte, Angular |
Философски SWR ближе к 'хук для чтения данных с правильным кэшем', а Query - к 'фреймворк для всего серверного состояния'. Это не делает один лучше другого: меньший API SWR быстрее освоить и легче по весу, а более полный Query закрывает сложные сценарии без сторонних дополнений. Выбор зависит от того, насколько богаты потребности проекта по работе с данными.
- SWR ближе по духу — Минимальный API, малый вес, фокус на чтении. Хорош, когда основное это получать и показывать данные с фоновой сверкой, а мутации простые.
- TanStack Query ближе по духу — Полный набор: оптимистичные мутации, инфинит, зависимые запросы, devtools. Хорош, когда работа с данными сложная и нужен один инструмент на всё.
Обе библиотеки используют стратегию stale-while-revalidate, поэтому базовое чтение выглядит почти одинаково: ключ плюс фетчер. Различия проступают на сложных сценариях - мутации с откатом, бесконечная прокрутка, отладка кэша.
В чём ключевое различие между SWR и TanStack Query?
Когда какой инструмент уместнее
Выбор сводится к богатству потребностей по данным. Если проект в основном читает и показывает - дашборд, профиль, документация, виджеты - и мутации простые, SWR закрывает задачу меньшим API и весом. Особенно естественно он ложится в Next.js, потому что обе вещи от Vercel и хорошо подогнаны друг к другу. Если же в проекте много изменений данных с оптимистичными обновлениями, бесконечные ленты, зависимые цепочки запросов и нужна отладка кэша - полнота TanStack Query экономит время.
| Сценарий | Уместнее | Почему |
|---|---|---|
| Дашборд и документация в Next.js | SWR | Чтение с фоновой сверкой, тот же вендор, малый вес |
| Виджеты и счётчики, простые мутации | SWR | Минимальный API закрывает потребность |
| Много оптимистичных мутаций с откатом | TanStack Query | Развитый useMutation с колбэками из коробки |
| Бесконечные ленты и зависимые запросы | TanStack Query | useInfiniteQuery и enabled встроены |
| Нужна серьёзная отладка кэша | TanStack Query | Полноценные devtools в комплекте |
Важно, что неправильного выбора между ними почти нет: оба ведут серверный кэш корректно. Вопрос в том, не упрётся ли проект в потолок SWR на сложных сценариях и стоит ли тащить больший вес Query там, где хватает чтения. Решает не мода, а профиль нагрузки на данные.
Для какого проекта SWR подходит лучше, чем более полный TanStack Query?
Связь с другими темами
Урок про лёгкую альтернативу. Дальше тема раскрывается так:
- Server-state против client-state — SWR ведёт серверное состояние как кэш - то самое разделение, что вынесено в опорный урок
- TanStack Query: основы — Прямой конкурент с тем же назначением, но большим набором возможностей
Итог
- SWR это библиотека Vercel для серверного состояния, и её имя - это стратегия stale-while-revalidate: показать устаревший кэш сразу, сверить с сервером в фоне
- Стратегия пришла из HTTP-директивы Cache-Control (RFC 5861), а SWR перенесла её в хук useSWR с ключом и функцией-фетчером
- По сравнению с TanStack Query: SWR меньше и проще, ядро сосредоточено на чтении. У Query шире встроенный движок мутаций, инфинит-запросов и devtools
- SWR уместен в лёгких интерфейсах, особенно в Next.js от того же вендора, где хватает чтения с фоновой сверкой
- TanStack Query уместен, когда нужны развитые мутации, оптимистичные обновления, бесконечные и зависимые запросы и инструменты отладки из коробки
Связанные уроки
- sm-30-server-vs-client-state — SWR ведёт серверное состояние как кэш, поэтому базовое разделение server-state и client-state нужно заранее
- sm-31-tanstack-query — TanStack Query решает ту же задачу серверного кэша, и сравнение двух инструментов помогает выбрать