Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Продвинутый Nuxt 3
nuxt_advanced

Продвинутый Nuxt 3

Middleware, плагины, Nuxt modules, SEO с useHead, оптимизация изображений, runtimeConfig и деплой

Продвинутый Nuxt 3

В этой теме рассмотрим продвинутые возможности Nuxt 3: middleware, плагины, модули, SEO-инструменты, оптимизацию изображений, переменные окружения и деплой.

#Route Middleware — маршрутный middleware

Route middleware в Nuxt — это функции, которые выполняются перед навигацией к определённому маршруту. Это аналог navigation guards в Vue Router, но с более удобной интеграцией.

Существуют три вида маршрутного middleware:

Inline middleware определяется прямо в компоненте страницы через definePageMeta:

<!-- pages/dashboard.vue --> <script setup> definePageMeta({ middleware: [ async (to, from) => { const authStore = useAuthStore() if (!authStore.isLoggedIn) { return navigateTo('/login') } } ] }) </script>

Named middleware хранится в папке middleware/ и переиспользуется:

// middleware/auth.ts export default defineNuxtRouteMiddleware((to, from) => { const authStore = useAuthStore() if (!authStore.isLoggedIn) { // navigateTo — глобальная функция Nuxt для редиректа return navigateTo({ path: '/login', query: { redirect: to.fullPath } }) } })
// middleware/admin.ts export default defineNuxtRouteMiddleware((to, from) => { const authStore = useAuthStore() if (!authStore.user?.role === 'admin') { throw createError({ statusCode: 403, statusMessage: 'Доступ запрещён' }) } })

Использование named middleware в страницах:

<!-- pages/admin/users.vue --> <script setup> definePageMeta({ // Middleware выполняются по порядку middleware: ['auth', 'admin'] }) </script>

Global middleware — файлы с суффиксом .global.ts применяются ко всем маршрутам автоматически:

// middleware/analytics.global.ts export default defineNuxtRouteMiddleware((to, from) => { // Трекинг всех переходов if (process.client) { analytics.track('page_view', { path: to.path }) } })

#Server Middleware — серверный middleware

Серверный middleware работает на уровне HTTP-сервера Nitro, до выполнения маршрутов. Используется для логирования, CORS, аутентификации через куки:

// server/middleware/auth.ts export default defineEventHandler(async (event) => { // Этот код выполняется для КАЖДОГО запроса к серверу // Пропускаем публичные маршруты const publicRoutes = ['/api/auth/login', '/api/auth/register', '/api/public'] if (publicRoutes.some(route => event.path.startsWith(route))) { return } // Пропускаем не-API маршруты if (!event.path.startsWith('/api/')) { return } const token = getCookie(event, 'access_token') || getHeader(event, 'Authorization')?.replace('Bearer ', '') if (!token) { throw createError({ statusCode: 401, statusMessage: 'Не авторизован' }) } try { const payload = verifyJWT(token, process.env.JWT_SECRET) // Добавляем данные пользователя в контекст запроса event.context.auth = { userId: payload.sub, role: payload.role } } catch { throw createError({ statusCode: 401, statusMessage: 'Токен недействителен' }) } })
// server/middleware/logger.ts export default defineEventHandler((event) => { const start = Date.now() const method = event.method const path = event.path // onAfterResponse вызывается после отправки ответа event.waitUntil( Promise.resolve().then(() => { const duration = Date.now() - start console.log(`${method} ${path} — ${duration}ms`) }) ) })

#Plugins — плагины

Плагины в Nuxt позволяют добавлять глобальные Vue-плагины, регистрировать глобальные компоненты, расширять nuxtApp и выполнять код при инициализации приложения:

// plugins/myPlugin.ts // Этот плагин выполняется при старте приложения (SSR и клиент) export default defineNuxtPlugin((nuxtApp) => { // Регистрируем Vue плагин nuxtApp.vueApp.use(MyVuePlugin) // Добавляем глобальные свойства nuxtApp.provide('formatDate', (date: Date) => { return new Intl.DateTimeFormat('ru-RU').format(date) }) // Добавляем глобальный обработчик ошибок nuxtApp.vueApp.config.errorHandler = (error, instance, info) => { console.error('Vue error:', error, info) // Отправляем в Sentry/другой сервис мониторинга } })

Использование провайденного значения в компоненте:

<script setup> // { $formatDate } автоматически доступно через provide const { $formatDate } = useNuxtApp() const formattedDate = $formatDate(new Date()) </script>

Client-only и server-only плагины — суффиксы в именах файлов:

// plugins/analytics.client.ts ← только на клиенте export default defineNuxtPlugin(() => { // window доступен только на клиенте window.dataLayer = window.dataLayer || [] // Инициализируем Google Analytics })
// plugins/database.server.ts ← только на сервере export default defineNuxtPlugin(async (nuxtApp) => { // Подключаемся к БД только на сервере const db = await connectDatabase(process.env.DATABASE_URL) nuxtApp.provide('db', db) })

Async плагины — для операций, требующих ожидания:

// plugins/auth.ts export default defineNuxtPlugin(async (nuxtApp) => { const authStore = useAuthStore() // Восстанавливаем сессию при старте try { await authStore.restoreSession() } catch { // Тихо игнорируем — пользователь просто не залогинен } })

#Nuxt Modules — модули

Модули — это пакеты, которые расширяют возможности Nuxt. Они добавляют компоненты, composables, серверные маршруты и настройки конфигурации:

// nuxt.config.ts — подключение модулей export default defineNuxtConfig({ modules: [ '@pinia/nuxt', // Pinia store management '@nuxtjs/tailwindcss', // Tailwind CSS '@nuxt/image', // Оптимизация изображений '@nuxtjs/color-mode', // Dark mode '@nuxtjs/i18n', // Интернационализация 'nuxt-svgo' // SVG как компоненты ], // Конфигурация конкретного модуля colorMode: { preference: 'system', fallback: 'light' }, image: { quality: 80, domains: ['cdn.example.com'] } })

Создание простого кастомного модуля:

// modules/hello/index.ts import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'hello', configKey: 'hello' }, defaults: { greeting: 'Привет' }, setup(options, nuxt) { const resolver = createResolver(import.meta.url) // Добавляем плагин addPlugin(resolver.resolve('./plugin')) // Добавляем composable nuxt.options.imports.dirs ||= [] nuxt.options.imports.dirs.push(resolver.resolve('./composables')) } })

#SEO: useHead, useSeoMeta, definePageMeta

Nuxt предоставляет удобные composables для управления <head> страницы:

<!-- pages/about.vue --> <script setup> // useHead — полный контроль над <head> useHead({ title: 'О нас — My Company', meta: [ { name: 'description', content: 'Мы создаём отличные продукты' }, { name: 'keywords', content: 'компания, продукты, разработка' } ], link: [ { rel: 'canonical', href: 'https://example.com/about' } ], script: [ // Добавить скрипт для конкретной страницы ] }) </script>

useSeoMeta — более удобный API с TypeScript-поддержкой для всех SEO-метатегов:

<!-- pages/blog/[slug].vue --> <script setup> const route = useRoute() const { data: post } = await useFetch(`/api/posts/${route.params.slug}`) // useSeoMeta с динамическими данными useSeoMeta({ title: () => `${post.value?.title} | My Blog`, description: () => post.value?.excerpt, // Open Graph ogTitle: () => post.value?.title, ogDescription: () => post.value?.excerpt, ogImage: () => post.value?.coverImage, ogUrl: () => `https://example.com/blog/${route.params.slug}`, ogType: 'article', // Twitter Card twitterCard: 'summary_large_image', twitterTitle: () => post.value?.title, twitterImage: () => post.value?.coverImage }) </script>

Глобальные SEO-настройки в nuxt.config.ts:

export default defineNuxtConfig({ app: { head: { htmlAttrs: { lang: 'ru' }, titleTemplate: '%s — My App', // %s заменяется на title страницы meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { name: 'description', content: 'Описание сайта по умолчанию' } ] } } })

definePageMeta для статических мета-данных страницы:

<script setup> definePageMeta({ title: 'Контакты', // Для маршрута (доступно в middleware) description: 'Свяжитесь с нами', layout: 'default', middleware: ['auth'], // Кастомные данные для middleware/layout requiresAuth: true, roles: ['admin', 'moderator'] }) </script>

#NuxtImage — оптимизация изображений

Модуль @nuxt/image автоматически оптимизирует изображения: изменяет размер, конвертирует в WebP, ленивую загрузку:

npm install @nuxt/image
// nuxt.config.ts export default defineNuxtConfig({ modules: ['@nuxt/image'], image: { quality: 80, // Разрешённые внешние домены domains: ['images.unsplash.com', 'cdn.example.com'], // Предустановленные размеры screens: { xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280 } } })

Использование <NuxtImg> и <NuxtPicture> в шаблонах:

<template> <!-- NuxtImg — оптимизированный img тег --> <NuxtImg src="/uploads/photo.jpg" alt="Фото" width="800" height="600" :modifiers="{ quality: 90 }" loading="lazy" /> <!-- Responsive изображение с разными размерами --> <NuxtImg src="/hero.jpg" sizes="100vw sm:50vw lg:400px" alt="Hero" /> <!-- NuxtPicture — picture тег с fallback --> <NuxtPicture src="/photo.jpg" :imgAttrs="{ class: 'rounded-lg' }" alt="Фото в современных форматах" /> </template>

#Environment Variables: runtimeConfig

Nuxt разделяет переменные окружения на серверные (недоступны клиенту) и публичные:

// nuxt.config.ts export default defineNuxtConfig({ runtimeConfig: { // Только на сервере — доступны в server routes и серверных плагинах jwtSecret: process.env.JWT_SECRET, databaseUrl: process.env.DATABASE_URL, smtpPassword: process.env.SMTP_PASSWORD, public: { // Доступны и на сервере и на клиенте apiBase: process.env.API_BASE || '/api', appVersion: '1.0.0', googleMapsKey: process.env.GOOGLE_MAPS_KEY } } })

Файл .env:

JWT_SECRET=super-secret-key-min-32-chars
DATABASE_URL=postgresql://user:pass@localhost/mydb
SMTP_PASSWORD=email-password
API_BASE=https://api.example.com
GOOGLE_MAPS_KEY=AIza...

Использование в компонентах и server routes:

<!-- Компонент (клиент + сервер) — только public --> <script setup> const config = useRuntimeConfig() // config.public.apiBase — доступно // config.jwtSecret — undefined на клиенте (только на сервере!) const { data } = await useFetch(`${config.public.apiBase}/posts`) </script>
// server/api/protected.ts — server route — все переменные доступны export default defineEventHandler(async (event) => { const config = useRuntimeConfig() // config.jwtSecret — доступно! const payload = verifyJWT(token, config.jwtSecret) return payload })

#Deployment — деплой

Nuxt создаёт оптимизированную сборку в зависимости от выбранного режима.

SSR деплой на Node.js — наиболее мощный вариант с поддержкой всех возможностей:

# Сборка для production npx nuxi build # Запуск node .output/server/index.mjs

Dockerfile для SSR-деплоя:

FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-alpine AS runtime WORKDIR /app COPY --from=builder /app/.output ./output ENV NODE_ENV=production ENV PORT=3000 EXPOSE 3000 CMD ["node", "output/server/index.mjs"]

SSG деплой — статические файлы на любой хостинг:

# Генерация всех страниц npx nuxi generate # Файлы в .output/public/ — загружаем на Vercel/Netlify/S3

Vercel деплой — автоматически определяет Nuxt и настраивает SSR:

// vercel.json (опционально — Vercel всё умеет сам) { "framework": "nuxt" }

Netlify деплой — через файл конфигурации:

# netlify.toml [build] command = "npm run generate" publish = ".output/public" [[redirects]] from = "/*" to = "/index.html" status = 200

Nuxt поддерживает концепцию пресетов деплоя — автоматически настраивает сборку под конкретную платформу:

// nuxt.config.ts — выбор пресета export default defineNuxtConfig({ nitro: { preset: 'vercel' // или 'netlify', 'cloudflare', 'aws-lambda', 'node-server' } })

При деплое на Vercel или Netlify пресет выбирается автоматически по CI/CD переменным.

#Полезные паттерны для продакшена

Несколько важных практик для продакшен Nuxt-приложений.

Обработка глобальных ошибок через error.vue:

<!-- error.vue — глобальная страница ошибки --> <template> <div> <h1>{{ error.statusCode }}</h1> <p>{{ error.statusMessage }}</p> <button @click="handleError">На главную</button> </div> </template> <script setup> const props = defineProps({ error: Object }) function handleError() { clearError({ redirect: '/' }) } </script>

Защита от ошибок в компонентах через <NuxtErrorBoundary>:

<template> <NuxtErrorBoundary @error="logError"> <template #error="{ error, clearError }"> <p>Компонент упал: {{ error.message }}</p> <button @click="clearError()">Попробовать снова</button> </template> <HeavyComponent /> </NuxtErrorBoundary> </template>

Далее: Тестирование Nuxt приложений