Жизненный цикл приложения в Nuxt 3
Nuxt 3 не просто добавляет роутинг и SSR поверх Vue 3 — он определяет собственный жизненный цикл приложения. Понимание этого цикла важно, потому что от него зависят ответы на вопросы: куда положить инициализацию библиотеки, как защитить роут, когда загружать данные и почему код падает на сервере, хотя работает в браузере.
В этой статье разберём три основных механизма расширения жизненного цикла — плагины, middleware и hooks, — а также общий порядок их выполнения при старте приложения, SSR и клиентской навигации.
Содержание
- Общая схема жизненного цикла
- Плагины
- Route middleware
- Runtime hooks
- Порядок выполнения
- SSR, CSR и hydration
- Лучшие практики и антипаттерны
- Ключевые тезисы для интервью
- Заключение
- Полезные ссылки
Общая схема жизненного цикла
Жизненный цикл Nuxt 3 можно разделить на три фазы:
- Инициализация приложения — создание Vue/Nuxt инстанса, загрузка плагинов, регистрация middleware.
- Обработка запроса / навигации — выполнение middleware, резолвинг роута, загрузка данных, рендеринг страницы.
- Runtime hooks — точки расширения, через которые можно подписаться на события типа
app:created,page:start,page:finish.
Ключевой объект во всём этом — nuxtApp. Он создаётся один раз при старте приложения и доступен в плагинах, middleware, composables и компонентах через useNuxtApp().
const nuxtApp = useNuxtApp();
nuxtApp содержит ссылки на Vue-приложение, роутер, маршрут, runtime config, провайдеры ($http, $dayjs и т.п.), а также метод hook() для подписки на события.
Плагины
Плагины в Nuxt — это способ запустить код при старте приложения: подключить внешнюю библиотеку, настроить HTTP-клиент, инициализировать аналитику, зарегистрировать директивы, добавить провайдеры в nuxtApp.
Где создавать
Файлы плагинов размещаются в папке plugins/:
plugins/
├── auth.ts # плагин аутентификации
├── vue-toastify.client.ts
└── sentry.server.ts
Nuxt автоматически находит и регистрирует все файлы из plugins/.
Простой плагин
// plugins/hello.ts
export default defineNuxtPlugin(() => {
console.log('Hello from plugin');
});
Плагин получает nuxtApp первым аргументом:
// plugins/api.ts
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig();
const $api = $fetch.create({
baseURL: config.public.apiBase,
});
return {
provide: {
api: $api,
},
};
});
После этого $api доступен в любом месте:
<script setup>
const { $api } = useNuxtApp();
const { data } = await useFetch('/posts', { $fetch: $api });
</script>
Режим выполнения плагинов
Плагины могут выполняться на сервере, клиенте или везде. Режим задаётся суффиксом в имени файла:
| Суффикс | Где выполняется |
|---|---|
.ts | И на сервере, и на клиенте (по умолчанию) |
.client.ts | Только в браузере |
.server.ts | Только на сервере |
// plugins/ga.client.ts — безопасно используем window
export default defineNuxtPlugin(() => {
if (process.client) {
// инициализация Google Analytics
}
});
Совет: суффикс
.client.tsнадёжнее, чем ручная проверкаprocess.clientвнутри универсального плагина. Nuxt просто не включит плагин в серверный бандл.
Порядок выполнения плагинов
По умолчанию плагины выполняются в алфавитном порядке имён файлов. Чтобы управлять порядком явно, можно:
- Переименовать файл с числовым префиксом:
plugins/
├── 01.init.ts
├── 02.api.ts
└── 03.auth.ts
- Использовать
dependsOnвdefineNuxtPlugin:
// plugins/auth.ts
export default defineNuxtPlugin({
name: 'auth',
dependsOn: ['api'], // ждёт плагин с name: 'api'
setup(nuxtApp) {
// код
},
});
- Асинхронные плагины: если плагин возвращает
Promise, Nuxt дождётся его выполнения перед запуском следующего плагина.
export default defineNuxtPlugin(async () => {
const config = await loadRemoteConfig();
return { provide: { config } };
});
Что возвращать из плагина
provide— добавляет свойства вnuxtApp(доступны как$name).hooks— удобная обёртка для подписки на runtime hooks внутри плагина.- Ничего — если плагин просто выполняет side-effect.
export default defineNuxtPlugin({
setup(nuxtApp) {
return {
provide: {
hello: (msg: string) => `Hello, ${msg}`,
},
};
},
});
Плагин vs Vue plugin
Не путайте Nuxt-плагин (defineNuxtPlugin) с Vue-плагином (app.use(...)). Первый — это точка входа в инфраструктуру Nuxt, второй — стандартный механизм Vue. Внутри Nuxt-плагина вы можете использовать Vue-плагин:
// plugins/toast.client.ts
import Toast from 'vue-toastification';
import 'vue-toastification/dist/index.css';
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(Toast, { position: 'top-right' });
});
Route middleware
Route middleware — это код, который выполняется перед входом на определённый роут. Используется для защиты страниц, редиректов, проверки прав, загрузки предварительных данных.
Виды middleware
| Вид | Где объявляется | Как подключается |
|---|---|---|
| Именованная | middleware/auth.ts | definePageMeta({ middleware: 'auth' }) |
| Глобальная | middleware/auth.global.ts | Автоматически для всех роутов |
| Анонимная | Прямо в странице | definePageMeta({ middleware: [...] }) |
Именованный middleware
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const { loggedIn } = useUserSession();
if (!loggedIn.value) {
return navigateTo('/login');
}
});
<!-- pages/admin.vue -->
<script setup>
definePageMeta({
middleware: 'auth',
});
</script>
Глобальный middleware
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to) => {
const { loggedIn } = useUserSession();
const publicRoutes = ['/login', '/register'];
if (!loggedIn.value && !publicRoutes.includes(to.path)) {
return navigateTo('/login');
}
});
Глобальный middleware выполняется при каждой навигации, включая первую загрузку страницы.
Анонимный middleware
<script setup>
definePageMeta({
middleware: [
(to) => {
if (to.params.id === '0') {
return navigateTo('/');
}
},
],
});
</script>
Анонимный middleware полезен для разовых проверок, но сложную логику лучше выносить в именованный файл.
Редирект и ошибка
Внутри middleware используются:
navigateTo(path)— клиентский или серверный редирект.abortNavigation()— отмена текущей навигации.throw createError({ statusCode: 403 })— показать страницу ошибки.
export default defineNuxtRouteMiddleware((to) => {
if (to.path.startsWith('/admin')) {
throw createError({
statusCode: 403,
statusMessage: 'Доступ запрещён',
});
}
});
Middleware работает на сервере и клиенте
Важно: при SSR middleware выполняется на сервере во время первого запроса. При последующих клиентских переходах — в браузере. Поэтому middleware не должен безусловно использовать window, localStorage, document.
Runtime hooks
Nuxt предоставляет систему runtime hooks — точек расширения, на которые можно подписаться через nuxtApp.hook() или декларативно внутри плагина.
Где подписываться
// плагин
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:finish', () => {
console.log('Страница отрендерена');
});
});
<!-- компонент -->
<script setup>
const nuxtApp = useNuxtApp();
nuxtApp.hook('page:finish', () => {
// ...
});
</script>
Основные runtime hooks
| Hook | Когда срабатывает |
|---|---|
app:created | Vue приложение создано, но ещё не смонтировано |
app:beforeMount | Перед монтированием на клиенте |
app:mounted | Приложение смонтировано на клиенте |
app:rendered | После SSR-рендеринга на сервере |
app:error | При возникновении ошибки |
app:data:refresh | При принудительном обновлении данных |
page:start | Перед началом рендеринга страницы |
page:finish | После завершения рендеринга страницы |
page:transition:finish | После завершения transition анимации страницы |
link:prefetch | При prefetch ссылки <NuxtLink> |
vue:setup | При вызове setup каждого Vue компонента |
vue:error | При ошибке в Vue (например, в lifecycle hook) |
Примеры использования
// плагин аналитики
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:finish', () => {
// Отправить событие просмотра страницы
sendPageView(useRoute().fullPath);
});
});
// обработка ошибок Vue
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('vue:error', (err) => {
logError(err);
});
});
Nitro hooks
На серверной части есть отдельная система hooks Nitro. Они задаются в nuxt.config.ts:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
hooks: {
'render:response': (response, { event }) => {
console.log('Response rendered for', event.node.req.url);
},
},
},
});
К Nitro hooks относятся request, beforeResponse, afterResponse, render:response, close и другие. Их используют для логирования, кэширования, модификации ответа.
Порядок выполнения
Понимание порядка execution order помогает решать проблемы вроде «плагин не видит данные» или «middleware срабатывает раньше, чем инициализирована авторизация».
При первой загрузке страницы (SSR)
- Nitro получает HTTP-запрос.
- Выполняются серверные middleware из
server/middleware/. - Создаётся
nuxtApp. - Срабатывает hook
app:created. - Запускаются плагины (в алфавитном порядке / по
dependsOn), включая.server.tsи универсальные. - Создаётся Vue приложение и роутер.
- Выполняется route middleware.
- Резолвится роут.
- Запускаются
useAsyncData/useFetchдля страницы. - Срабатывает hook
page:start. - Происходит SSR-рендеринг страницы.
- Срабатывают hooks
app:renderedиpage:finish. - HTML отправляется клиенту.
Гидратация на клиенте
- Браузер получает HTML.
- Выполняются клиентские плагины (
.client.tsи универсальные на клиенте). - Vue гидрирует приложение.
- Срабатывает hook
app:mounted. - Приложение готово к интеракциям.
При клиентской навигации
- Пользователь кликает
<NuxtLink>или вызываетnavigateTo(). - Выполняется route middleware.
- Резолвится новый роут.
- Запускаются
useAsyncData/useFetchновой страницы. - Срабатывает hook
page:start. - Рендерится новая страница.
- Срабатывает hook
page:finish. - Если настроены transitions —
page:transition:finish.
Схема в таблице
| Этап | Сервер (первый запрос) | Клиент (навигация) |
|---|---|---|
| HTTP / событие | Nitro + server middleware | navigateTo / <NuxtLink> |
| Создание nuxtApp | ✅ | уже создан |
app:created | ✅ | — |
| Плагины | ✅ (server + all) | ✅ (client + all) |
| Route middleware | ✅ | ✅ |
| asyncData / useFetch | ✅ | ✅ |
page:start | ✅ | ✅ |
| Рендеринг | SSR | CSR |
page:finish | ✅ | ✅ |
app:rendered | ✅ | — |
app:mounted | — | ✅ (первый раз) |
SSR, CSR и hydration
При SSR код выполняется дважды: сначала на сервере для генерации HTML, затем на клиенте для гидратации. Это влияет на lifecycle.
Клиент-only код
Если нужно выполнить код только в браузере:
<script setup>
onMounted(() => {
// выполняется только на клиенте
document.title = 'Client only';
});
</script>
Или через плагин .client.ts:
// plugins/chart.client.ts
export default defineNuxtPlugin(() => {
// window гарантированно доступен
});
Server-only код
Для кода, который должен выполняться только на сервере, используйте .server.ts плагины или server middleware/API.
Hydration mismatch
Если состояние, полученное на сервере, отличается от состояния на клиенте, возникает hydration mismatch. Это приводит к ошибкам в консоли и потенциально к некорректному UI.
Причины:
- Использование
Date.now()илиMath.random()безonMounted. - Разный контент в зависимости от
process.client/process.serverвнутри шаблона. - Разные данные при повторном запросе на клиенте.
Лечение:
- Клиент-only контент оборачивать в
<ClientOnly>или выводить вonMounted. - Данные для страницы загружать через
useAsyncData/useFetch— Nuxt передаст их с сервера на клиент.
Лучшие практики и антипаттерны
✅ Делайте
- Используйте плагины для инициализации библиотек и добавления провайдеров в
nuxtApp. - Управляйте порядком плагинов через префиксы имён или
dependsOn. - Используйте
.client.tsи.server.tsсуффиксы, чтобы избежать runtime-проверок. - Помещайте защиту роутов в route middleware, а не в
onMountedкомпонента. - Используйте
navigateTo()для редиректов в middleware. - Подписывайтесь на
page:finishдля аналитики и пост-обработки страницы. - Оборачивайте клиентский код в
onMountedили<ClientOnly>. - Логируйте ошибки через
vue:errorhook.
❌ Не делайте
- Не вызывайте
useNuxtApp()вне синхронного Vue-контекста (например, в асинхронных колбэках безcallWithNuxt). - Не используйте
window/documentв универсальных плагинах без проверки. - Не размещайте тяжёлую бизнес-логику в middleware — middleware должен быть лёгким и синхронным/быстрым.
- Не забывайте, что глобальный middleware срабатывает при каждой навигации, включая первую загрузку.
- Не игнорируйте hydration mismatch — они ломают интерактивность.
- Не путайте Nuxt-плагины и Vue-плагины — если нужно
app.use(...), делайте это внутриdefineNuxtPlugin.
Ключевые тезисы для интервью
- Жизненный цикл Nuxt 3 включает инициализацию приложения, выполнение плагинов, middleware, data fetching, SSR/рендеринг и runtime hooks.
- Плагины — точка входа для инициализации библиотек и провайдеров; находятся в
plugins/, автоматически регистрируются, могут быть.client.ts,.server.tsили универсальными. - Порядок плагинов задаётся алфавитно или через
dependsOnвdefineNuxtPlugin; асинхронные плагины блокируют запуск следующих. - Route middleware бывает именованным, глобальным (
*.global.ts) и анонимным; используется для защиты роутов и редиректов. - Middleware выполняется и на сервере при первом запросе, и на клиенте при навигации.
- Runtime hooks —
app:created,app:mounted,app:rendered,page:start,page:finish,vue:error,link:prefetchи другие. - Порядок выполнения при SSR: серверные middleware →
nuxtApp→app:created→ плагины → route middleware → asyncData →page:start→ рендер →page:finish/app:rendered. - При hydration клиентские плагины запускаются повторно, поэтому side-effects нужно либо защищать, либо размещать в
.client.ts. - Hydration mismatch возникает, когда клиентское состояние отличается от серверного; лечится через
useAsyncData/useFetchи<ClientOnly>/onMounted. - Nuxt-плагин — это обёртка вокруг инфраструктуры Nuxt; внутри него можно вызывать Vue-плагины через
nuxtApp.vueApp.use(...).
Заключение
Жизненный цикл Nuxt 3 — это набор соглашений и точек расширения, которые делают фреймворк full-stack. Плагины отвечают за инициализацию, middleware — за контроль доступа к роутам, hooks — за подписку на события. Понимание порядка их выполнения помогает избежать типичных ошибок: неработающего SSR, hydration mismatch, неправильного порядка инициализации и утечек клиентского кода на сервер.
Следующие статьи раздела продолжат погружение в Nuxt: серверную часть Nitro, data fetching, состояние, SEO, обработку ошибок, слои и модули.
Полезные ссылки
- Nuxt 3 Plugins — официальная документация по плагинам
- Nuxt 3 Middleware — документация по route middleware
- Nuxt 3 Runtime Hooks — справочник runtime hooks
- Nuxt 3 Lifecycle — жизненный цикл приложения
- Nitro Hooks — hooks серверной части Nitro
- Vue 3 Lifecycle Hooks — жизненный цикл Vue компонентов