Представьте, приложение на Nuxt стало медленно открываться или часто перерисовываться. Как вы будете искать причину?
Начните с Lighthouse и nuxi analyze для измерения. Высокий TTFB — смотрите серверные запросы и routeRules с cache/isr. Частые перерисовки — Vue DevTools Profiler и аудит реактивных зависимостей.
Диагностика производительности Nuxt-приложения
Медленное открытие и частые перерисовки — разные симптомы с разными причинами. Диагностику нужно проводить последовательно, начиная с измерений.
Этап 1: Измерить, не угадывать
# Lighthouse в headless-режиме для стабильных цифр
npx lighthouse https://your-app.com --output=json --chrome-flags="--headless" | \
jq '{fcp: .audits["first-contentful-paint"].displayValue, lcp: .audits["largest-contentful-paint"].displayValue, tbt: .audits["total-blocking-time"].displayValue}'
Зафиксируйте метрики до изменений: FCP, LCP, TBT, TTI, TTFB. Без baseline невозможно оценить прогресс.
Этап 2: Медленное открытие — диагностика TTFB
Высокий TTFB (>600 ms) указывает на проблему на сервере:
- Медленные запросы к БД или внешним API в server routes — добавьте логирование времени.
- Отсутствие кеширования: используйте
routeRulesсcacheилиisr. - Cold start serverless-функции — рассмотрите keep-alive или переход на Node.js-сервер.
// nuxt.config.ts — включить кеш для статичных маршрутов
export default defineNuxtConfig({
routeRules: {
'/blog/**': { isr: 3600 }, // ISR — кеш на 1 час
'/api/catalog': { cache: { maxAge: 60 } },
},
})
Этап 3: Медленное открытие — размер бандла
# Анализ бандла
npx nuxi analyze
Ищите: дублирование зависимостей, тяжёлые библиотеки в initial chunk (lodash, moment, тяжёлые UI-библиотеки). Решения: dynamic import, замена на lighter-аналоги (date-fns вместо moment), tree-shaking настройка.
Этап 4: Частые перерисовки — Vue DevTools Profiler
Откройте Vue DevTools (отдельное расширение браузера) → вкладка Performance → запустите запись → воспроизведите проблему. Ищите компоненты с высокой частотой рендеринга.
// Типичная причина: реактивный объект пересоздаётся на каждый рендер
// Плохо:
const options = computed(() => ({ page: currentPage.value, limit: 20 }))
watch(options, fetchData) // options — новый объект при каждом computed, watch триггерится всегда
// Хорошо:
watch(currentPage, fetchData) // отслеживать только нужный примитив
Этап 5: Профилирование SSR
// server/api/debug-timing.get.ts
export default defineEventHandler(async (event) => {
const start = Date.now()
const data = await expensiveQuery()
console.log(`Query time: ${Date.now() - start}ms`)
return data
})
Этап 6: Проверить useFetch кеширование
// Если useFetch повторяет запросы при каждой навигации — проверьте ключ
const { data } = await useFetch('/api/products', {
key: `products-${categoryId}`, // уникальный ключ = нет лишних запросов
getCachedData: (key, nuxtApp) => nuxtApp.payload.data[key] // использовать payload
})
Подводные камни
- Lighthouse в режиме dev-сервера даёт нерелевантные цифры — всегда измеряйте production-сборку.
- Частые перерисовки в списках: компонент без
:keyили с нестабильным ключом заставляет Vue уничтожать и пересоздавать DOM-элементы вместо патчинга. useAsyncDataбезlazy: trueблокирует навигацию до завершения запроса — пользователь видит белый экран вместо skeleton.- Тяжёлые watcher-цепочки: watch → изменение state → computed → watch — цикл может триггериться тысячи раз в секунду без очевидной причины.
- Nuxt Image без
width/heightвызывает layout shift (CLS) — всегда указывайте размеры. - Импорт иконочных библиотек целиком (например,
import * as Icons from '@heroicons/vue') раздувает бандл — используйте named imports. - SSR-рендеринг тяжёлых markdown-файлов через Nuxt Content без кеша нагружает CPU при каждом запросе.
What hurts your answer
- Сразу обвинять Nuxt, не проверив соседние слои системы
- Чинить симптом без минимального воспроизведения и evidence
- Не учитывать версии, конфигурацию, окружение и recent changes
What they're listening for
- Умеет локализовать проблему вокруг Nuxt
- Двигается от симптома к гипотезам и проверкам
- Отличает баг инструмента от ошибки использования или окружения