Nuxt 4 у 2026 році: Нова структура каталогів та міграція з Nuxt 3

Повний посібник з міграції на Nuxt 4: нова структура app/, singleton-шар отримання даних, поверхнева реактивність за замовчуванням, розділення TypeScript-контексту, нормалізовані імена компонентів, Vue Router v5, зміни в управлінні head та контрольний список міграції.

Nuxt 4 нова структура каталогів та схема міграції з Nuxt 3

Nuxt 4 являє собою найбільше архітектурне оновлення фреймворку з моменту виходу третьої версії. Нова структура каталогу app/, singleton-модель отримання даних, поверхнева реактивність за замовчуванням та розділений TypeScript-контекст змінюють підхід до організації й супроводу Vue-застосунків. Для розробників, які працюють з екосистемою Vue та Nuxt, глибоке розуміння цих змін є критично важливим як для повсякденної роботи, так і для підготовки до технічних співбесід. У цьому матеріалі розглядається кожна ключова зміна Nuxt 4 з практичними прикладами коду та покроковими інструкціями щодо міграції з Nuxt 3.

Автоматизований інструмент міграції

Nuxt 4 надає офіційний codemod для автоматичної реструктуризації каталогів. Перед початком ручної міграції варто запустити npx codemod@latest nuxt/4/file-structure, який автоматизує більшу частину структурних змін. Проте оновлення composable, конфігурація TypeScript та управління мета-тегами потребують ручного втручання.

Нова структура каталогу app/

Найпомітніша зміна в Nuxt 4 полягає у переміщенні всіх клієнтських та серверних файлів застосунку до спеціалізованого каталогу app/. У Nuxt 3 компоненти, сторінки, лейаути, middleware та плагіни розміщувалися безпосередньо в кореневому каталозі проєкту. Nuxt 4 чітко розмежовує код застосунку від конфігураційних файлів, публічних ресурсів та серверного коду.

text
my-nuxt-app/
├─ app/
│  ├─ assets/
│  ├─ components/
│  ├─ composables/
│  ├─ layouts/
│  ├─ middleware/
│  ├─ pages/
│  ├─ plugins/
│  ├─ utils/
│  ├─ app.vue
│  ├─ app.config.ts
│  └─ error.vue
├─ content/
├─ public/
├─ shared/         # New: code shared between app and server
├─ server/
└─ nuxt.config.ts

Новий каталог shared/ є важливим архітектурним доповненням. Він дозволяє спільно використовувати типи, допоміжні функції та константи між кодом застосунку (app/) і серверним кодом (server/). У Nuxt 3 розробники змушені були дублювати типи на обох сторонах або створювати тимчасові рішення з відносними шляхами імпорту. Каталог shared/ вирішує цю проблему на структурному рівні, забезпечуючи єдине джерело істини для спільного коду.

Файл nuxt.config.ts залишається в кореневому каталозі проєкту й не переміщується до app/. Аналогічно, каталоги server/ та public/ зберігають свої поточні розташування. Це розмежування чітко визначає зони відповідальності: app/ містить код, що виконується у браузері та на SSR-сервері, server/ зберігає бекенд-логіку, а shared/ слугує мостом між цими двома шарами.

Покрокова міграція з Nuxt 3

Процес міграції з Nuxt 3 на Nuxt 4 складається з кількох послідовних етапів. Першим кроком є оновлення фреймворку до останньої версії та дедуплікація залежностей.

bash
# Upgrade Nuxt and deduplicate dependencies
npx nuxt upgrade --dedupe

Далі запускається офіційний codemod, який автоматично реструктурує файлову систему відповідно до нового стандарту. Codemod переносить файли до каталогу app/, оновлює шляхи імпорту та адаптує конфігурацію.

bash
# Automate the directory restructuring
npx codemod@latest nuxt/4/file-structure

Для проєктів, які не можуть одразу перейти на нову структуру каталогів, Nuxt 4 пропонує опцію зворотної сумісності. Налаштування srcDir та dir.app у конфігурації дозволяє зберегти плоску структуру під час поступового переходу.

nuxt.config.tstypescript
export default defineNuxtConfig({
  srcDir: '.',
  dir: { app: 'app' },
})

Однак це тимчасове рішення. Команда Nuxt однозначно рекомендує повний перехід на структуру app/, оскільки майбутні версії фреймворку оптимізуватимуться саме під цю організацію. Крім того, інструменти екосистеми, зокрема Nuxt DevTools та модулі спільноти, вже починають приймати нову структуру як стандартну.

Singleton-шар отримання даних та реактивні ключі

Nuxt 4 вносить фундаментальну зміну в роботу composable useAsyncData та useFetch. У Nuxt 3 виклик одного й того ж ключа даних з різних компонентів міг спричинити дублювання мережевих запитів та неузгодженість стану. Nuxt 4 впроваджує singleton-патерн: кожен унікальний ключ даних пов'язується рівно з одним екземпляром отримання даних, незалежно від кількості компонентів, що використовують ці дані.

Додатково useAsyncData отримує новий параметр getCachedData з контекстним об'єктом ctx, який повідомляє причину повторного виклику. Це дає змогу точно контролювати, коли використовувати кешовані дані, а коли виконувати новий запит.

app/composables/useProductData.tstypescript
export function useProductData(productId: string) {
  return useAsyncData(
    `product-${productId}`,
    () => $fetch(`/api/products/${productId}`),
    {
      getCachedData: (key, nuxtApp, ctx) => {
        // ctx.cause tells why the fetch is happening
        if (ctx.cause === 'refresh:manual') return undefined
        return nuxtApp.payload.data[key]
      },
    },
  )
}

Параметр ctx.cause може набувати значень 'refresh:manual' (ручне оновлення, ініційоване розробником), 'refresh:hook' (оновлення, ініційоване хуком життєвого циклу) або 'navigation' (навігація між сторінками). Завдяки цьому composable може приймати обгрунтовані рішення щодо стратегії кешування: наприклад, завжди використовувати кеш при навігації, але примусово виконувати запит при ручному оновленні.

Singleton-патерн також усуває стани перегонів (race conditions). Коли кілька компонентів одночасно запитують однакові дані, Nuxt 4 виконує лише один запит і розподіляє результат між усіма підписниками. Такий підхід зменшує мережеве навантаження та забезпечує узгодженість даних у всьому застосунку.

Поверхнева реактивність за замовчуванням

Одна з найвпливовіших змін у Nuxt 4 полягає в тому, що дані, повернуті useAsyncData та useFetch, за замовчуванням обгортаються у shallowRef замість ref. У Nuxt 3 використовувалася глибока реактивність, а це означало, що будь-яка зміна вкладених властивостей об'єкта автоматично ініціювала оновлення компонента. У Nuxt 4 повторний рендер відбувається лише при повній заміні значення value.

app/pages/dashboard.vuetypescript
<script setup lang="ts">
// Data is now a shallowRef by default
const { data: metrics } = await useFetch('/api/dashboard/metrics')

// Direct property mutation won't trigger reactivity
// metrics.value.visits = 100  // Won't trigger re-render

// Replace the entire value to trigger updates
metrics.value = { ...metrics.value, visits: 100 }

// Or opt into deep reactivity for this specific call
const { data: settings } = await useFetch('/api/settings', {
  deep: true,
})
</script>

Ця зміна має прямий вплив на продуктивність. Глибока реактивність створює проксі для кожного вкладеного об'єкта та масиву, що спричиняє значне навантаження на пам'ять та процесор при роботі з великими структурами даних. Поверхнева реактивність повністю усуває ці витрати, оскільки система реактивності Vue відстежує лише посилання верхнього рівня.

Для команд, що мігрують з Nuxt 3, це означає необхідність перевірки всіх ділянок коду, де вкладені властивості даних, отриманих через useFetch або useAsyncData, змінюються безпосередньо. Кожну таку мутацію потрібно замінити повною заміною значення (через spread-оператор) або явним увімкненням глибокої реактивності за допомогою опції deep: true.

Готовий до співбесід з Vue.js / Nuxt.js?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Розділення TypeScript-контексту (Context Splitting)

Замість єдиного глобального tsconfig.json Nuxt 4 розділяє конфігурацію TypeScript на чотири окремі контексти. Кожен контекст має власні правила розв'язання типів, що усуває проблему видимості серверних типів у клієнтському коді та навпаки.

json
{
  "files": [],
  "references": [
    { "path": "./.nuxt/tsconfig.app.json" },
    { "path": "./.nuxt/tsconfig.server.json" },
    { "path": "./.nuxt/tsconfig.shared.json" },
    { "path": "./.nuxt/tsconfig.node.json" }
  ]
}

Контекст app охоплює код у каталозі app/, включаючи компоненти, сторінки та composable. Контекст server обслуговує файли в каталозі server/ з доступом до типів Node.js та серверних API. Контекст shared забезпечує підтримку типів для каталогу shared/, видимих як з app, так і з server. Контекст node покриває конфігураційні файли, зокрема nuxt.config.ts.

Це розділення вимагає зміни способу запуску перевірки типів. Прапорець -b (режим компіляції проєктних посилань) для vue-tsc стає обов'язковим.

bash
# Before (Nuxt 3)
nuxt prepare && vue-tsc --noEmit

# After (Nuxt 4)
nuxt prepare && vue-tsc -b --noEmit

Без прапорця -b компілятор TypeScript не розпізнає міжпроєктні посилання й повідомлятиме про помилки відсутніх типів. Це одна з найчастіше пропущених змін під час міграції, яка призводить до збоїв у CI/CD-конвеєрах.

Нормалізовані імена компонентів та Vue Router v5

Nuxt 4 змінює правила іменування автоматично імпортованих компонентів. Компоненти у підкаталогах тепер отримують імена на основі повного шляху каталогу, а не лише імені файлу. Наприклад, компонент components/dashboard/MetricsCard.vue у Nuxt 3 був доступний як DashboardMetricsCard або MetricsCard. У Nuxt 4 дійсним є лише повне нормалізоване ім'я.

Ця зміна безпосередньо впливає на конфігурацію <KeepAlive>, де імена компонентів мають бути явно вказані у масивах include або exclude.

vue
<!-- app/pages/dashboard.vue -->
<template>
  <NuxtPage :keepalive="{
    include: ['DashboardMetricsCard', 'DashboardRecentActivity'],
  }" />
</template>

Необхідно перевірити імена компонентів, використані в keepalive, <Transition> та динамічних компонентах (<component :is="...">), на відповідність новим нормалізованим іменам. Інакше KeepAlive припинить кешування компонентів, а анімовані переходи перестануть працювати.

Паралельно з цим Nuxt 4 інтегрує Vue Router v5, який вносить зміни в API навігації та механізми навігаційних гардів. Метод router.resolve() тепер повертає розширений об'єкт з додатковими метаданими. Навігаційні гарди отримали покращену типізацію параметрів. Командам, що використовують складні патерни маршрутизації, необхідно перевірити сумісність своїх гардів та middleware з новим API.

Критичні зміни в управлінні head (Unhead v2)

Nuxt 4 переходить на Unhead v2, що вносить критичні зміни в управління мета-тегами та елементами <head>. Властивості vmid, hid, children та body, які використовувалися в Nuxt 3 для визначення й дедуплікації тегів, видалено.

app/pages/product/[id].vuetypescript
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)

// Unhead v2: removed vmid, hid, children, body props
useSeoMeta({
  title: () => product.value?.name ?? 'Product',
  ogTitle: () => product.value?.name ?? 'Product',
  description: () => product.value?.description ?? '',
  ogImage: () => product.value?.imageUrl ?? '',
})
</script>

Окрім того, деякі плагіни Unhead, зокрема TemplateParamsPlugin та AliasSortingPlugin, більше не завантажуються за замовчуванням. Якщо проєкт використовує шаблонні параметри у мета-тегах (наприклад, %s | Назва сайту), їх необхідно явно зареєструвати як Nuxt-плагін.

app/plugins/unhead.tstypescript
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'

export default defineNuxtPlugin({
  setup() {
    const unhead = injectHead()
    unhead.use(TemplateParamsPlugin)
    unhead.use(AliasSortingPlugin)
  },
})

Симптом відсутньої реєстрації цих плагінів є досить підступним: шаблони заголовків не обробляються, і користувач бачить необроблений текст із символами %s замість сформованого заголовка. Ця проблема може не впливати на роботу застосунку в середовищі розробки, тому її складно виявити, поки не буде проаналізовано SEO-метрики в Google Search Console.

Контрольний список міграції та типові пастки

Нижче наведено повний контрольний список для команд, що мігрують з Nuxt 3 на Nuxt 4. Кожен пункт має бути перевірений та підтверджений перед розгортанням у виробниче середовище.

  1. Оновлення Nuxt -- запуск npx nuxt upgrade --dedupe та перевірка сумісності всіх залежностей з Nuxt 4
  2. Реструктуризація каталогів -- запуск codemod або ручне переміщення файлів до каталогів app/, shared/ та server/
  3. Перевірка шляхів імпорту -- контроль оновлення всіх шляхів імпорту після переміщення файлів
  4. Оновлення composable отримання даних -- перегляд усіх використань useAsyncData та useFetch з урахуванням нового API getCachedData та singleton-патерну
  5. Адаптація до shallow reactivity -- виявлення та виправлення всіх місць, де вкладені властивості даних змінюються безпосередньо
  6. Конфігурація TypeScript -- оновлення tsconfig.json до формату проєктних посилань та перехід vue-tsc на режим -b
  7. Імена компонентів -- перевірка відповідності імен у keepalive, <Transition> та динамічних компонентах новим нормалізованим іменам
  8. Мета-теги (Unhead v2) -- видалення застарілих властивостей (vmid, hid, children, body) та реєстрація необхідних плагінів
  9. E2E-тести -- запуск повного набору наскрізних тестів з урахуванням змін навігації Vue Router v5
  10. CI/CD-конвеєр -- оновлення команд збірки та перевірки типів у конфігурації конвеєра

Найтиповіші пастки під час міграції:

  • Забуті мутації shallowRef -- код виду data.value.property = x, що безпосередньо змінює вкладені властивості, не ініціює повторний рендер. Це найчастіше повідомлювана проблема після міграції
  • Відсутній прапорець -b -- пропуск -b у команді vue-tsc спричиняє хибні помилки типів, які не видно локально, але з'являються в CI
  • Ненормалізовані імена у KeepAlive -- KeepAlive мовчки ігнорує нерозпізнані імена, що призводить до втрати кешування без видимої помилки
  • Відсутні плагіни Unhead -- непрацюючі шаблони заголовків перетворюються на SEO-проблему, яку можна виявити лише при аналізі результатів Google Search Console

Для поглиблення знань з екосистеми Vue та Nuxt рекомендується звернутися до питання для співбесіди Vue/Nuxt та посібник з SSR та статичної генерації.

Висновки

Nuxt 4 є значним кроком в еволюції фреймворку, що вносить зміни на кожному рівні процесу розробки. Основні висновки для підготовки до міграції та технічних співбесід:

  • Нова структура каталогу app/ чітко відокремлює код застосунку від конфігурації та серверних ресурсів; каталог shared/ усуває дублювання типів між клієнтом та сервером
  • Singleton-шар отримання даних забезпечує точний контроль над стратегією кешування через патерн getCachedData та контекстний об'єкт ctx, запобігаючи дублюванню мережевих запитів
  • Поверхнева реактивність за замовчуванням суттєво підвищує продуктивність для великих структур даних, однак вимагає свідомої адаптації коду, що змінює вкладені властивості
  • Розділений TypeScript-контекст забезпечує строгу ізоляцію типів між клієнтським, серверним та спільним кодом, усуваючи цілий клас типових помилок
  • Нормалізовані імена компонентів та інтеграція Vue Router v5 вимагають перегляду конфігурації KeepAlive, анімованих переходів та навігаційних гардів
  • Unhead v2 зобов'язує явно реєструвати шаблонні плагіни та очищати застарілі властивості мета-тегів
  • Офіційний codemod автоматизує реструктуризацію каталогів, проте зміни у composable, TypeScript та мета-тегах потребують ручної перевірки та адаптації

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Теги

#nuxt
#vue
#migration
#typescript
#tutorial

Поділитися

Пов'язані статті