Как Nuxt управляет code splitting и lazy loading компонентов?
Nuxt автоматически делит бандл по маршрутам (pages/), а компоненты можно загружать лениво через префикс Lazy или defineAsyncComponent. Анализ чанков — npx nuxi analyze.
Code Splitting и Lazy Loading в Nuxt
Nuxt использует Vite (или webpack) в качестве бандлера и автоматически разбивает приложение на чанки. Каждый маршрут получает свой отдельный чанк, который загружается только при переходе пользователя на соответствующую страницу.
Автоматический code splitting по маршрутам
Все файлы в директории pages/ автоматически становятся отдельными чанками. Nuxt не требует явной конфигурации — разбивка по маршрутам включена по умолчанию.
Lazy-компоненты через префикс Lazy
Любой компонент из components/ можно загрузить лениво, добавив префикс Lazy к имени тега:
// components/HeavyChart.vue — существует в проекте
// pages/dashboard.vue
<template>
<div>
<!-- Компонент загрузится только при монтировании -->
<LazyHeavyChart v-if="showChart" :data="chartData" />
<button @click="showChart = true">Показать график</button>
</div>
</template>
<script setup lang="ts">
const showChart = ref(false)
const chartData = ref([])
</script>
defineAsyncComponent для ручного контроля
// Явный lazy import с fallback и обработкой ошибок
const HeavyComponent = defineAsyncComponent({
loader: () => import('~/components/HeavyChart.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorFallback,
delay: 200, // ms перед показом loadingComponent
timeout: 5000, // ms до показа errorComponent
})
Экспериментальный Payload Extraction
В Nuxt 3.x доступен флаг payloadExtraction, который выносит данные страниц в отдельные JSON-файлы при статической генерации:
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
payloadExtraction: true,
},
})
Анализ бандла
# Запустить анализатор размера чанков
npx nuxi analyze
Команда открывает визуальный отчёт (rollup-plugin-visualizer), где видно, какие модули попали в какой чанк и почему.
Prefetch и Preload
Nuxt автоматически добавляет <link rel="prefetch"> для маршрутов, видимых в viewport через <NuxtLink>. Можно управлять этим поведением:
// nuxt.config.ts
export default defineNuxtConfig({
router: {
options: {
linkActiveClass: 'active',
},
},
experimental: {
crossOriginPrefetch: true,
},
})
Подводные камни
- Префикс
Lazyсоздаёт отдельный чанк только если компонент не используется нигде в eager-режиме — если тот же компонент импортирован без Lazy в другом месте, чанк всё равно будет в основном бандле. - Слишком мелкая нарезка: сотни крохотных чанков по 1–2 KB хуже одного чанка из-за накладных расходов HTTP/1.1; с HTTP/2 граница сдвигается, но всё равно важно не дробить без смысла.
- Hydration mismatch при использовании
LazyComponentс SSR: если компонент рендерится на сервере, добавьте<ClientOnly>или используйтеv-ifс правильной логикой. defineAsyncComponentи авто-импорт несовместимы: нельзя написатьconst C = defineAsyncComponent(...)и ожидать, что Nuxt подхватит его как авто-импорт.- Динамические импорты с переменными (
import(`./${name}.vue`)) создают wildcard-чанки и ломают tree-shaking — всегда используйте статические строки. - При
ssr: falseвесь SPA грузится одним entry-point — code splitting по маршрутам всё равно работает, но первый бандл больше из-за отсутствия серверного рендеринга. - Vite chunking strategy по умолчанию может объединять модули неожиданным образом; для контроля нужен
build.rollupOptions.output.manualChunksв nuxt.config.
Common mistakes
- Путать code splitting and lazy loading с похожим API из соседнего фреймворка.
- Не объяснять, где код выполняется: сервер, клиент, build step или runtime.
- Игнорировать влияние на hydration, cache, bundle size или безопасность.
What the interviewer is testing
- Точно объясняет назначение механизма «code splitting and lazy loading».
- Показывает корректный минимальный пример без выдуманных API.
- Называет ограничения, failure modes и production-компромиссы.