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

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

@potapov_me

Платформа

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

Контент

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

Компания

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

Аккаунт

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

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

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Производительность и оптимизация
nuxt_performance

Производительность и оптимизация

Web Vitals, bundle анализ, lazy-компоненты, NuxtImage, кэширование server routes, ISR и Lighthouse CI

Производительность и оптимизация Nuxt приложений

Производительность — это не финальный шаг, а архитектурное решение. Правильные инструменты Nuxt позволяют достичь отличных показателей Web Vitals без глубоких оптимизаций, если использовать их с самого начала.

#Web Vitals: что измерять и зачем

Google использует Core Web Vitals для ранжирования. Три ключевых метрики:

LCP (Largest Contentful Paint) — время до отрисовки самого большого элемента на странице (обычно hero-изображение или заголовок). Цель: менее 2.5 секунды. Основные причины плохого LCP: большие неоптимизированные изображения, медленные server routes, блокирующий JS.

INP (Interaction to Next Paint) — задержка от действия пользователя до визуального ответа. Заменил FID в 2024 году. Цель: менее 200 мс. Причины плохого INP: тяжёлые event handlers, блокировка главного потока.

CLS (Cumulative Layout Shift) — суммарное смещение элементов во время загрузки. Цель: менее 0.1. Основные причины: изображения без размеров, шрифты без font-display swap, динамически вставляемый контент.

Инструменты измерения: Lighthouse в Chrome DevTools, Web Vitals расширение, npm run build && npx lighthouse https://yoursite.com.

#Анализ bundle: nuxi analyze

Перед оптимизацией нужно понять что занимает место:

# Запускает сборку с анализом бандла npx nuxi analyze # Или добавьте в package.json # "analyze": "nuxt analyze"

После сборки откроется интерактивный граф (rollup-plugin-visualizer). Ищите:

  • Дублированные модули (один пакет несколько раз)
  • Неожиданно большие зависимости (moment.js, lodash целиком)
  • Код, который должен быть lazy-loaded, но попал в основной чанк
// nuxt.config.ts — подключение анализатора вручную import { visualizer } from 'rollup-plugin-visualizer' export default defineNuxtConfig({ vite: { plugins: [ visualizer({ filename: 'dist/stats.html', open: true, gzipSize: true }) ] } })

#Code splitting и lazy-компоненты

Nuxt автоматически разбивает код по маршрутам. Каждая страница — отдельный чанк, загружаемый по запросу. Но компоненты на странице по умолчанию включаются в её чанк.

Для тяжёлых компонентов используйте defineAsyncComponent или префикс Lazy:

<script setup lang="ts"> import { defineAsyncComponent } from 'vue' // Явный async component с loading/error состояниями const HeavyChart = defineAsyncComponent({ loader: () => import('@/components/HeavyChart.vue'), loadingComponent: ChartSkeleton, // Пока грузится errorComponent: ErrorBoundary, // Если ошибка delay: 200, // Задержка перед показом loading timeout: 10000 // Таймаут в мс }) </script> <template> <!-- Prefix Lazy — то же самое, но автоматически --> <LazyHeavyChart v-if="showChart" :data="chartData" /> <!-- Или явно --> <HeavyChart v-if="showChart" :data="chartData" /> </template>

Хорошие кандидаты для lazy loading: графики и диаграммы, редакторы (markdown, rich text), модальные окна и диалоги, компоненты ниже fold (не видны без прокрутки).

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

@nuxt/image — модуль для автоматической оптимизации изображений: конвертация в WebP/AVIF, изменение размера, lazy loading:

npm install @nuxt/image
// nuxt.config.ts export default defineNuxtConfig({ modules: ['@nuxt/image'], image: { // Провайдеры для внешних источников providers: { cloudinary: { baseURL: 'https://res.cloudinary.com/your-cloud/' } }, // Разрешённые внешние домены domains: ['images.unsplash.com', 'avatars.githubusercontent.com'] } })

Использование в шаблоне:

<template> <!-- NuxtImg автоматически: WebP, lazy, правильный srcset --> <NuxtImg src="/images/hero.jpg" alt="Hero изображение" width="1200" height="600" sizes="sm:100vw md:80vw lg:1200px" format="webp" quality="80" loading="eager" /> <!-- Для изображений ниже fold — lazy по умолчанию --> <NuxtImg src="/images/product.jpg" alt="Продукт" width="400" height="300" /> </template>

Критически важно указывать width и height — это предотвращает CLS (прыжок лейаута при загрузке).

#Шрифты с @nuxt/fonts

npm install @nuxt/fonts

@nuxt/fonts автоматически загружает шрифты, добавляет font-display: swap и генерирует font-size fallback для предотвращения CLS:

// nuxt.config.ts export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { defaults: { weights: [400, 600, 700], styles: ['normal'], subsets: ['cyrillic', 'latin'] } } })
/* В CSS просто используйте шрифт — модуль найдёт его автоматически */ body { font-family: 'Inter', sans-serif; }

Модуль автоматически загружает Inter с Google Fonts, оптимизирует его и добавляет preload.

#Кэширование server routes

Один из самых эффективных способов ускорить API — кэшировать результаты тяжёлых запросов:

// server/api/popular-courses.get.ts // cachedEventHandler кэширует результат handler функции export default cachedEventHandler( async (event) => { // Эта функция выполнится только при cache miss const courses = await db.courses.findMany({ where: { isPublished: true }, orderBy: { enrollmentCount: 'desc' }, take: 10, include: { author: true, tags: true } }) return courses }, { maxAge: 60 * 5, // TTL 5 минут name: 'popular-courses', // Уникальный ключ кэша getKey: () => 'all', // Фиксированный ключ (не зависит от запроса) } )

Для кэширования отдельных функций (не обработчиков):

// server/utils/courses.ts export const getCourseBySlug = defineCachedFunction( async (slug: string) => { return await db.courses.findBySlug(slug) }, { maxAge: 60 * 10, // 10 минут name: 'course-by-slug', getKey: (slug: string) => slug // Ключ зависит от аргумента } )

Инвалидация кэша при обновлении данных:

// server/api/admin/courses/[id].patch.ts import { useStorage } from 'nitropack/runtime' export default defineEventHandler(async (event) => { const id = getRouterParam(event, 'id') const body = await readBody(event) await db.courses.update({ where: { id }, data: body }) // Инвалидируем кэш const storage = useStorage() await storage.removeItem(`nitro:functions:course-by-slug:${body.slug}.json`) return { success: true } })

#ISR и routeRules: гибридный рендеринг

routeRules позволяет задать стратегию рендеринга для каждого маршрута:

// nuxt.config.ts export default defineNuxtConfig({ routeRules: { // Главная — ISR, перегенерируется раз в 60 секунд '/': { isr: 60 }, // Статические маркетинговые страницы '/about': { static: true }, '/pricing': { static: true }, // Блог — ISR с большим TTL '/blog/**': { isr: 3600 }, // Dashboard — только SSR, никакого кэша '/dashboard/**': { ssr: true }, // API — CORS и кэш заголовки '/api/**': { cors: true, headers: { 'cache-control': 's-maxage=60' } }, // Редирект '/old-page': { redirect: '/new-page' } } })

ISR (Incremental Static Regeneration) — лучшее из двух миров: страница генерируется статически (быстро, как CDN), но перегенерируется с заданным интервалом. Идеально для блогов, каталогов, лендингов.

swr (Stale While Revalidate) аналогичен ISR, но использует HTTP-заголовки вместо серверной логики — работает с CDN без специальной поддержки платформы.

#Preloading и prefetching

<NuxtLink prefetch> предзагружает страницу когда ссылка появляется в viewport:

<template> <!-- Prefetch — загрузить чанк когда ссылка видима --> <NuxtLink to="/courses" prefetch>Курсы</NuxtLink> <!-- Preload — загрузить немедленно (для критических ссылок) --> <NuxtLink to="/dashboard" preload>Дашборд</NuxtLink> </template>

Глобальная настройка prefetch:

// nuxt.config.ts export default defineNuxtConfig({ router: { options: { // Prefetch всех ссылок при наведении linkActiveClass: 'active-link' } }, experimental: { // Включить prefetch по умолчанию renderJsonPayloads: true } })

#Payload редукция: pick и transform

useFetch возвращает весь ответ API, но часто нужна только часть данных. pick и transform уменьшают размер payload:

// Без оптимизации — весь объект курса const { data: course } = await useFetch(`/api/courses/${slug}`) // С pick — только нужные поля const { data: course } = await useFetch(`/api/courses/${slug}`, { pick: ['title', 'description', 'author', 'price'] }) // С transform — произвольная трансформация на клиенте const { data: courses } = await useFetch('/api/courses', { transform: (courses) => courses.map(c => ({ id: c.id, title: c.title, slug: c.slug, price: c.price })) })

transform выполняется перед сохранением в payload. Это уменьшает как размер передаваемых данных, так и объём памяти на клиенте.

#Мониторинг: Nuxt DevTools и Lighthouse CI

Nuxt DevTools показывает в реальном времени: какие компоненты используются, какие запросы делает приложение, состояние Pinia, размер payload:

# DevTools включены по умолчанию в dev режиме npm run dev # Откройте http://localhost:3000 и нажмите иконку Nuxt снизу страницы

Lighthouse CI в GitHub Actions:

# .github/workflows/lighthouse.yml - name: Audit with Lighthouse CI run: | npm install -g @lhci/cli npm run build npm run preview & sleep 5 lhci autorun env: LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

Конфигурация порогов в lighthouserc.json:

{ "ci": { "assert": { "preset": "lighthouse:no-pwa", "assertions": { "categories:performance": ["error", {"minScore": 0.9}], "categories:seo": ["error", {"minScore": 0.9}], "first-contentful-paint": ["warn", {"maxNumericValue": 2000}], "largest-contentful-paint": ["error", {"maxNumericValue": 2500}] } } } }

Настройте эти пороги так, чтобы они соответствовали реальным требованиям — слишком строгие пороги создают false negatives, слишком мягкие не защищают от регрессий производительности.

Далее: Деплой Nuxt приложений