SSR-гидрация: как работает автоматически
\nNuxt передаёт состояние Pinia через useNuxtApp().payload. При SSR-рендере store заполняется на сервере, сериализуется в HTML (в тег <script id=\"__NUXT_DATA__\">), и клиент гидрирует его без повторных запросов. Это работает «из коробки» — вручную ничего сериализовать не нужно.
Инициализация store на сервере через plugins
\n// plugins/pinia-init.server.ts\nexport default defineNuxtPlugin(async () => {\n const userStore = useUserStore();\n // Заполняем store данными при каждом SSR-запросе\n const event = useRequestEvent();\n const token = getCookie(event, 'auth_token');\n if (token) {\n await userStore.fetchUser(/* decode token -> id */);\n }\n});\n\n\nPersist состояния между сессиями (localStorage)
\nnpm install @pinia-plugin-persistedstate/nuxt\n// nuxt.config.ts\nexport default defineNuxtConfig({\n modules: ['@pinia/nuxt', '@pinia-plugin-persistedstate/nuxt'],\n});\n\n// stores/cart.ts\nexport const useCartStore = defineStore('cart', () => {\n const items = ref([]);\n return { items };\n}, {\n persist: {\n storage: piniaPluginPersistedstate.localStorage(),\n // На сервере localStorage недоступен — плагин автоматически пропускает\n },\n});\n \n\nПодводные камни
\n- \n
- Singleton store на сервере: при SSR каждый запрос должен получать свой экземпляр Pinia.
@pinia/nuxtсоздаёт его черезuseNuxtApp().$pinia— не обращайтесь к store вне контекста Nuxt-плагина или composable. \n - localStorage на сервере:
localStorageнедоступен при SSR. Если используетеpersist, убедитесь что плагин поддерживает SSR-режим, иначе получитеReferenceError. \n - useStore вне setup(): вызов store за пределами
setup()или Nuxt-плагина вызовет ошибку «no active Pinia». Инициализируйте store только внутри composable-функций. \n - Дублирование fetch: если store инициализируется в плагине на сервере и ещё раз в компоненте через
useAsyncData, данные будут запрошены дважды. Проверяйте состояние store перед повторным fetch. \n - Гидрация несоответствий: если сервер и клиент возвращают разные данные (таймзоны, random), React получит mismatch-ошибку. Store должен давать детерминированные данные при SSR. \n
- Circular imports: store может импортировать другой store через actions — следите за цикличными зависимостями, они приводят к undefined в runtime. \n
Как интегрируется Pinia в Nuxt 3? Как сохранить состояние store между SSR и клиентом?
Pinia подключается через модуль @pinia/nuxt. Для гидрации SSR→клиент state передаётся через payload Nuxt автоматически; для persistence используют pinia-plugin-persistedstate с SSR-safe опциями.
Подключение Pinia в Nuxt 3
Установите модуль и добавьте его в конфиг:
npm install pinia @pinia/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@pinia/nuxt'],
});
После этого все store-файлы из stores/ становятся авто-импортируемыми. Функция defineStore тоже авто-импортируется — дополнительных импортов в компонентах не нужно.
Создание store
// stores/user.ts
export const useUserStore = defineStore('user', () => {
const user = ref<{ id: number; name: string } | null>(null);
const isLoggedIn = computed(() => user.value !== null);
async function fetchUser(id: number) {
const data = await $fetch(`/api/users/${id}`);
user.value = data;
}
function logout() {
user.value = null;
}
return { user, isLoggedIn, fetchUser, logout };
});
Использование store в компонентах и composables
// components/UserProfile.vue
<script setup lang="ts">
const userStore = useUserStore();
await userStore.fetchUser(1);
</script>
SSR-гидрация: как работает автоматически
Nuxt передаёт состояние Pinia через useNuxtApp().payload. При SSR-рендере store заполняется на сервере, сериализуется в HTML (в тег <script id="__NUXT_DATA__">), и клиент гидрирует его без повторных запросов. Это работает «из коробки» — вручную ничего сериализовать не нужно.
Инициализация store на сервере через plugins
// plugins/pinia-init.server.ts
export default defineNuxtPlugin(async () => {
const userStore = useUserStore();
// Заполняем store данными при каждом SSR-запросе
const event = useRequestEvent();
const token = getCookie(event, 'auth_token');
if (token) {
await userStore.fetchUser(/* decode token -> id */);
}
});
Persist состояния между сессиями (localStorage)
npm install @pinia-plugin-persistedstate/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@pinia/nuxt', '@pinia-plugin-persistedstate/nuxt'],
});
// stores/cart.ts
export const useCartStore = defineStore('cart', () => {
const items = ref<CartItem[]>([]);
return { items };
}, {
persist: {
storage: piniaPluginPersistedstate.localStorage(),
// На сервере localStorage недоступен — плагин автоматически пропускает
},
});
Подводные камни
- Singleton store на сервере: при SSR каждый запрос должен получать свой экземпляр Pinia.
@pinia/nuxtсоздаёт его черезuseNuxtApp().$pinia— не обращайтесь к store вне контекста Nuxt-плагина или composable. - localStorage на сервере:
localStorageнедоступен при SSR. Если используетеpersist, убедитесь что плагин поддерживает SSR-режим, иначе получитеReferenceError. - useStore вне setup(): вызов store за пределами
setup()или Nuxt-плагина вызовет ошибку «no active Pinia». Инициализируйте store только внутри composable-функций. - Дублирование fetch: если store инициализируется в плагине на сервере и ещё раз в компоненте через
useAsyncData, данные будут запрошены дважды. Проверяйте состояние store перед повторным fetch. - Гидрация несоответствий: если сервер и клиент возвращают разные данные (таймзоны, random), React получит mismatch-ошибку. Store должен давать детерминированные данные при SSR.
- Circular imports: store может импортировать другой store через actions — следите за цикличными зависимостями, они приводят к undefined в runtime.
Common mistakes
- Путать Pinia in Nuxt с похожим API из соседнего фреймворка.
- Не объяснять, где код выполняется: сервер, клиент, build step или runtime.
- Игнорировать влияние на hydration, cache, bundle size или безопасность.
What the interviewer is testing
- Точно объясняет назначение механизма «Pinia in Nuxt».
- Показывает корректный минимальный пример без выдуманных API.
- Называет ограничения, failure modes и production-компромиссы.