State Management
Nano Stores
Сайт на Astro собирает островки из разных фреймворков: шапка на React, корзина на Vue, баннер на Svelte. Каждому островку нужно знать число товаров в корзине. Через стор React эти данные не расшарить: Vue-островок про React-контекст ничего не знает. Nano Stores задаёт вопрос: а что, если стор это просто атом в отдельном модуле, к которому одинаково подключаются и React, и Vue, и Svelte, и ванильный JS, а весит вся библиотека меньше килобайта.
- Astro-сайты с островами из разных фреймворков, которым нужно общее состояние корзины или темы
- Микрофронтенды: несколько приложений на одной странице делят авторизацию и настройки
- Дизайн-системы и виджеты, встраиваемые в чужие страницы без привязки к React
- Проекты с жёстким бюджетом на размер бандла, где важен каждый килобайт
- Постепенная миграция между фреймворками, где состояние должно пережить смену слоя представления
Предварительные знания
- Идея единого источника правды для общего состояния
- Подписка на изменения через слушателя и отписка от неё
- Базовое понимание того, что фреймворк это лишь слой представления над данными
Откуда взялись framework-agnostic атомы
Nano Stores создал Андрей Ситник, автор PostCSS и Autoprefixer, примерно в 2021 году. Замысел шёл от размера и независимости: стор должен весить сотни байт и не зависеть ни от одного UI-фреймворка, чтобы одни и те же данные могли читать React, Vue, Svelte, Preact и ванильный JS. Каждый стор это маленький атом со значением и списком подписчиков, а тонкие адаптеры подключают его к конкретному фреймворку. К 2026 году подход стал особенно заметен в экосистеме Astro, где острова из разных фреймворков на одной странице делят общее состояние именно через Nano Stores.
Атомы и карты как единицы состояния
Базовая единица в Nano Stores это atom: маленький стор для одного значения. Функция atom принимает начальное значение и возвращает объект с методами get и set, а также подпиской. Изменение идёт через set, чтение через get. Никаких провайдеров, никакой привязки к React: атом это обычный модуль.
Когда значений несколько и они логически связаны, удобнее map: стор-объект, у которого поля меняются по ключу через setKey. Это экономит лишние пересоздания: меняется одно поле, а подписчики на другие поля карты не дёргаются. По соглашению имена сторов начинают с символа доллара, чтобы отличать их от обычных значений.
Производные значения собирает computed: он берёт один или несколько сторов и вычисляет из них новое значение, которое само становится стором. Это аналог derived-состояния: цена с налогом считается из суммы корзины и пересчитывается, когда сумма меняется.
Чем map() отличается от atom() в Nano Stores?
Один стор для разных фреймворков
Ключевая идея в том, что сам стор ничего не знает о фреймворке. Он умеет хранить значение и оповещать подписчиков. Подключение к конкретному UI делают тонкие адаптеры. Для React это useStore из пакета nanostores/react, для Vue и Svelte свои хелперы. Один и тот же атом читается из всех них одновременно.
useStore подписывается на атом и перерисовывает компонент при изменении значения, а при размонтировании отписывается. Тот же `$theme` в Vue-компоненте читается через адаптер для Vue, в Svelte через автоподписку с символом доллара. Стор живёт в отдельном модуле, а слои представления лишь подключаются к нему.
Поскольку стор это обычный модуль, его можно тестировать без рендера компонентов: вызвать set, прочитать get, проверить подписку. Логика состояния отделена от UI, и это упрощает и тесты, и перенос между фреймворками.
Почему один и тот же стор Nano Stores может читаться из React, Vue и Svelte сразу?
Ниша 2026: Astro, микрофронтенды, кросс-фреймворк
Сильнее всего Nano Stores проявляется там, где на одной странице сосуществуют разные фреймворки. Главный пример это Astro с его островной архитектурой: статичная страница, а внутри интерактивные острова, каждый из которых может быть на своём фреймворке. Общее состояние между островами Astro рекомендует держать именно в Nano Stores, потому что стор переживает границу острова и фреймворка.
Похожая картина в микрофронтендах: несколько независимых приложений на одной странице делят авторизацию, тему, настройки. Стор размером в сотни байт не утяжеляет каждый микрофронтенд и не навязывает им общий фреймворк. Третий сценарий это постепенная миграция: при переезде с одного фреймворка на другой состояние в Nano Stores остаётся на месте, меняется только слой представления над ним.
| Сценарий | Почему Nano Stores |
|---|---|
| Astro-острова | Состояние переживает границу острова и фреймворка |
| Микрофронтенды | Сотни байт на стор, без навязанного общего фреймворка |
| Кросс-фреймворк виджет | Один атом читают React, Vue, Svelte, ванильный JS |
| Миграция фреймворка | Данные на месте, меняется только UI-слой |
| Жёсткий бюджет бандла | Размер библиотеки порядка сотен байт |
Когда всё приложение это один React без чужих фреймворков и без жёсткого лимита на бандл, перевес Nano Stores над Zustand или Jotai невелик. Их сила в независимости от фреймворка проявляется именно на стыке нескольких UI-слоёв, а не внутри одного.
В каком сценарии framework-agnostic природа Nano Stores даёт наибольшее преимущество?
Связь с другими темами
Этот урок про framework-agnostic состояние. Рядом стоят соседи:
- Единый источник правды — Nano Stores это один источник правды, который переживает границу между фреймворками
- Лёгкие сторы: сравнение — Nano Stores сравнивается с Zustand, Jotai и Valtio по размеру бандла и модели
- Zustand — Zustand привязан к React-хукам, Nano Stores независим от фреймворка - это и есть водораздел
Итог
- Nano Stores это крошечные атомы со значением и подписчиками, библиотека весит порядка сотен байт
- Стор не привязан ни к одному фреймворку: к нему подключаются React, Vue, Svelte, Preact и ванильный JS
- atom() хранит одно значение, map() удобен для объекта полей, computed() выводит производное значение
- Подписка через listen и subscribe, в React через useStore из адаптера nanostores/react
- Главная ниша 2026 года: Astro-острова, микрофронтенды и общее состояние между разными фреймворками
- Автор Андрей Ситник делал ставку на минимальный размер и независимость от UI-слоя
Связанные уроки
- sm-03-single-source-of-truth — Nano Stores это единый источник правды, общий для нескольких фреймворков сразу
- sm-21-lightweight-comparison — Nano Stores сравнивается с Zustand, Jotai и Valtio по размеру и ментальной модели
- rc-38-zustand-state — Zustand живёт внутри React, а Nano Stores работает независимо от фреймворка, и это ключевое отличие