Руководство по обновлению
Обновление Nuxt
Последний релиз
Чтобы обновить Nuxt до последнего релиза, используйте команду nuxt upgrade.
npx nuxt upgrade
yarn nuxt upgrade
pnpm nuxt upgrade
bun x nuxt upgrade
deno x nuxt upgrade
Канал ночных сборок
Чтобы использовать свежую сборку Nuxt и тестировать возможности до релиза, прочитайте руководство по каналу ночных сборок.
Тестирование Nuxt 5
Nuxt 5 сейчас в разработке. До релиза многие breaking changes Nuxt 5 можно опробовать, начиная с Nuxt 4.2+.
Подключение к поведению Nuxt 5
Сначала обновите Nuxt до последнего релиза.
Затем задайте future.compatibilityVersion, чтобы включить поведение Nuxt 5:
export default defineNuxtConfig({
future: {
compatibilityVersion: 5,
},
})
Когда future.compatibilityVersion равен 5, значения по умолчанию в конфигурации Nuxt меняются в сторону поведения Nuxt v5, в том числе:
- Vite Environment API: используется новый Vite Environment API для более гибкой настройки сборки
- Нормализованные имена страниц: имена компонентов страниц совпадают с именами маршрутов для согласованного поведения
<KeepAlive> clearNuxtStateсбрасывает к значениям по умолчанию:clearNuxtStateвосстанавливает состояние до начального значения вместоundefined- Неасинхронный
callHook:callHookможет вернутьvoidвместо всегда возвращаемогоPromise - Плейсхолдеры в виде комментариев: клиентские компоненты используют узлы-комментарии вместо
<div>как SSR-плейсхолдеры, что устраняет проблему гидратации со scoped-стилями - Другие улучшения и изменения Nuxt 5 по мере появления
future.compatibilityVersion: 5, периодически проверяйте эту страницу.Ниже перечислены существенные изменения и шаги миграции для обратной и прямой совместимости.
Миграция на Vite Environment API
🚦 Уровень влияния: Средний
Что изменилось
Nuxt 5 переходит на новый Environment API Vite 6, который формализует понятие окружений и даёт лучший контроль над конфигурацией для каждого окружения.
Раньше Nuxt использовал отдельные конфигурации Vite для клиента и сервера. Теперь — общую конфигурацию Vite с плагинами под конкретные окружения, которые через метод applyToEnvironment() нацеливаются на нужные окружения.
experimental.viteEnvironmentApi удалена.Ключевые изменения:
- Устарели окружение-специфичные вызовы
extendViteConfig(): опцииserverиclientвextendViteConfig()помечены как устаревшие и при использовании выводят предупреждения. - Изменилась регистрация плагинов: у Vite-плагинов, зарегистрированных через
addVitePlugin()и нацеленных только на одно окружение (черезserver: falseилиclient: false), не вызываются хукиconfigилиconfigResolved. - Общая конфигурация: хуки
vite:extendConfigиvite:configResolvedработают с общей конфигурацией, а не с раздельными client/server.
Зачем это сделано
Vite Environment API даёт:
- лучшую согласованность между dev и production
- более точный контроль над настройками по окружениям
- улучшенную производительность и архитектуру плагинов
- поддержку пользовательских окружений помимо client и server
Шаги миграции
1. Перейти на Vite-плагины
Рекомендуем использовать Vite-плагин вместо extendViteConfig, vite:configResolved и vite:extendConfig.
// До
extendViteConfig((config) => {
config.optimizeDeps.include.push('my-package')
}, { server: false })
nuxt.hook('vite:extendConfig' /* или vite:configResolved */, (config, { isClient }) => {
if (isClient) {
config.optimizeDeps.include.push('my-package')
}
})
// После
addVitePlugin(() => ({
name: 'my-plugin',
config (config) {
// здесь можно задать глобальную конфигурацию Vite
},
configResolved (config) {
// здесь доступна полностью разрешённая конфигурация Vite
},
configEnvironment (name, config) {
// здесь можно задать конфигурацию Vite для конкретного окружения
if (name === 'client') {
config.optimizeDeps ||= {}
config.optimizeDeps.include ||= []
config.optimizeDeps.include.push('my-package')
}
},
applyToEnvironment (environment) {
return environment.name === 'client'
},
}))
2. Перенести Vite-плагины на окружения
Вместо addVitePlugin с server: false или client: false используйте хук applyToEnvironment внутри плагина.
// До
addVitePlugin(() => ({
name: 'my-plugin',
config (config) {
config.optimizeDeps.include.push('my-package')
},
}), { client: false })
// После
addVitePlugin(() => ({
name: 'my-plugin',
config (config) {
// здесь можно задать глобальную конфигурацию Vite
},
configResolved (config) {
// здесь доступна полностью разрешённая конфигурация Vite
},
configEnvironment (name, config) {
// здесь можно задать конфигурацию Vite для конкретного окружения
if (name === 'client') {
config.optimizeDeps ||= {}
config.optimizeDeps.include ||= []
config.optimizeDeps.include.push('my-package')
}
},
applyToEnvironment (environment) {
return environment.name === 'client'
},
}))
Миграция на Vite 8
🚦 Уровень влияния: Средний
Что изменилось
Nuxt 5 переходит с Vite 7 на Vite 8: вместо esbuild и Rollup в качестве бандлера используется Rolldown. Сборки становятся заметно быстрее, но есть ряд breaking changes.
future.compatibilityVersion: 5. Чтобы проверить совместимость с Vite 8 заранее, добавьте в package.json переопределение версии "vite": "^8.0.0-beta.15".Большую часть миграции Nuxt выполняет сам, но имейте в виду следующее:
vite.esbuildиvite.optimizeDeps.esbuildOptionsустарели в пользуvite.oxcиvite.optimizeDeps.rolldownOptions. Vite 8 пока конвертирует их автоматически, но в будущем они будут удалены.build.rollupOptionsустарел в пользуbuild.rolldownOptions.- Поведение CommonJS interop изменилось. Если вы импортируете CJS-модули, см. руководство по миграции Vite 8.
Миграция на Nitro v3
🚦 Уровень влияния: Значительный
Что изменилось
Nuxt 5 переходит на Nitro v3 — крупную переработку серверного движка. Nitro v3 построен на srvx и h3 v2 и везде использует веб-стандартные API Request/Response. Это улучшает производительность и единообразие API, но ломает совместимость серверного кода.
Ниже — изменения, наиболее важные для разработчиков приложений и авторов модулей Nuxt.
Пакет и пути импорта
Пакет nitropack переименован в nitro. Изменились все пути импорта:
| До | После |
|---|---|
nitropack | nitro |
nitropack/types | nitro/types |
nitropack/runtime | nitro |
h3 (серверные утилиты) | nitro/h3 |
Автоимпорты в серверных маршрутах (defineEventHandler, getQuery, readBody, useRuntimeConfig и т.д.) работают как прежде.
Если в серверном коде есть явные импорты, обновите их:
- import { defineEventHandler, getQuery } from 'h3'
+ import { defineEventHandler, getQuery } from 'nitro/h3'
Для авторов модулей расширения типов должны указывать на новый путь модуля:
- declare module 'nitropack/types' {
+ declare module 'nitro/types' {
interface NitroRouteRules {
myModule?: { /* ... */ }
}
}
Обработка ошибок: status/statusText вместо statusCode/statusMessage
В h3 v2 свойства ошибок переименованы в соответствии с веб-стандартами:
createError({
- statusCode: 404,
- statusMessage: 'Not Found',
+ status: 404,
+ statusText: 'Not Found',
})
В серверных маршрутах вместо createError из h3 используется класс HTTPError:
- import { createError } from 'h3'
+ import { HTTPError } from 'nitro/h3'
export default defineEventHandler(() => {
- throw createError({ statusCode: 400, statusMessage: 'Bad request' })
+ throw new HTTPError({ status: 400, statusText: 'Bad request' })
})
app/) композабл createError от Nuxt по-прежнему работает и рекомендуется для выброса ошибок.Изменения Server Event API (h3 v2)
Объект H3Event теперь опирается на веб-стандартные API:
Свойства запроса:
- event.path // строка
+ event.url.pathname // объект URL — используйте .pathname, .search, .hash
- event.method // строка
+ event.req.method // через Web Request
- event.node.req.headers // Node.js IncomingHttpHeaders
+ event.req.headers // Web Headers API (.get(), .set(), .has())
Свойства ответа:
- event.node.res.statusCode = 200
+ event.res.status = 200
- event.node.res.statusMessage = 'OK'
+ event.res.statusText = 'OK'
- setResponseHeader(event, 'x-custom', 'value')
+ event.res.headers.set('x-custom', 'value')
- appendResponseHeader(event, 'set-cookie', cookie)
+ event.res.headers.append('set-cookie', cookie)
useRuntimeConfig() больше не принимает event
В Nitro v3 useRuntimeConfig() в серверных маршрутах не принимает (и не требует) аргумент event:
export default defineEventHandler((event) => {
- const config = useRuntimeConfig(event)
+ const config = useRuntimeConfig()
})
Правила маршрутов: statusCode переименован в status
Для правил редиректа изменилось имя свойства:
export default defineNuxtConfig({
routeRules: {
'/old-page': {
- redirect: { to: '/new-page', statusCode: 302 },
+ redirect: { to: '/new-page', status: 302 },
},
},
})
Дополнительно для авторов модулей
- Импорты плагинов Nitro: для явных импортов используйте
import { definePlugin } from 'nitro'(автоимпорты по-прежнему работают). - Хуки рантайма:
nitroApp.hooks.hook('beforeResponse', ...)иnitroApp.hooks.hook('afterResponse', ...)заменены наnitroApp.hooks.hook('response', ...). getRouteRules()изnitro/app: на сервере хелпер изменился сgetRouteRules(event)наgetRouteRules(method, pathname)и возвращает{ routeRules }.
Удаление experimental.externalVue
🚦 Уровень влияния: Минимальный
Что изменилось
Опция experimental.externalVue удалена. Зависимости компилятора Vue (@babel/parser, @vue/compiler-core, @vue/compiler-dom, @vue/compiler-ssr, estree-walker) при отключённом vue.runtimeCompiler теперь всегда подменяются мок-прокси в серверном бандле.
Зачем это сделано
После перехода на Nitro v3 зависимости по умолчанию попадают в серверный вывод целиком (в Nitro v2 node_modules внешне подключались). Опция externalVue изначально держала Vue внешней зависимостью, чтобы не дублировать копии Vue в бандле; в Nitro v3 всё равно всё бандлится, и опция стала no-op.
Серверные сборки Vue тянут полный компилятор, включая @babel/parser (~465 КБ) и другие пакеты, хотя они нужны только при vue.runtimeCompiler.
Постоянная подмена этих зависимостей моками уменьшает размер серверного бандла по умолчанию примерно на 860 КБ (~59%).
Шаги миграции
Если вы явно задавали experimental.externalVue, удалите эту опцию.
export default defineNuxtConfig({
experimental: {
- externalVue: false,
},
})
vue.runtimeCompiler: true настоящие пакеты компилятора по-прежнему включаются в сборку.@vitejs/plugin-vue-jsx стал опциональным
🚦 Уровень влияния: Минимальный
Что изменилось
@vitejs/plugin-vue-jsx больше не устанавливается по умолчанию вместе с @nuxt/vite-builder. Это опциональная peer-зависимость, подключаемая по требованию при появлении .jsx или .tsx в сборке.
Если в проекте есть JSX/TSX-компоненты, Nuxt сам предложит установить пакет.
Зачем это сделано
Плагин @vitejs/plugin-vue-jsx тянет тяжёлое дерево зависимостей (Babel, @vue/babel-plugin-jsx и др.), ненужное проектам без JSX. Опциональность уменьшает размер установки по умолчанию и ускоряет разрешение зависимостей для большинства проектов.
Шаги миграции
Если используются файлы .jsx или .tsx, добавьте @vitejs/plugin-vue-jsx в devDependencies:
npm install -D @vitejs/plugin-vue-jsx
yarn add -D @vitejs/plugin-vue-jsx
pnpm add -D @vitejs/plugin-vue-jsx
bun add -D @vitejs/plugin-vue-jsx
Либо Nuxt сам предложит установить пакет при первой обработке JSX/TSX в dev.
Если JSX не используется, ничего менять не нужно.
Удаление устаревшей поддержки _renderResponse
🚦 Уровень влияния: Минимальный
Что изменилось
ssrContext._renderResponse больше не проверяется как запасной вариант. Используется только внутренний ssrContext['~renderResponse'] (выставляется композаблом роутера Nuxt).
Зачем это сделано
Свойство _renderResponse оставляли для обратной совместимости после #33896, где внутренний API перешёл на ~renderResponse. В TODO было указано удалить это в Nuxt v5.
Шаги миграции
Если вы напрямую задавали ssrContext._renderResponse (это никогда не было публичным API), используйте ssrContext['~renderResponse']. Композабл роутера Nuxt уже использует новое свойство; через navigateTo или middleware маршрута менять ничего не нужно.
Неасинхронный callHook
🚦 Уровень влияния: Минимальный
Что изменилось
После обновления до hookable v6 callHook может вернуть void вместо всегда возвращаемого Promise<void>. Это заметно снижает накладные расходы: лишние Promise не создаются, если хуков нет или все они синхронные.
По умолчанию (при compatibilityVersion: 4) Nuxt оборачивает callHook в Promise.resolve(), чтобы цепочки .then() и .catch() продолжали работать. При compatibilityVersion: 5 эта обёртка снимается.
Зачем это сделано
В hookable v6 callHook примерно в 20–40 раз быстрее, так как не создаёт Promise, когда он не нужен. Выигрывают приложения с большим числом вызовов хуков.
Шаги миграции
Если вы или модули используете callHook с .then() или .catch(), перейдите на await:
- nuxtApp.callHook('my:hook', data).then(() => { ... })
+ await nuxtApp.callHook('my:hook', data)
- nuxtApp.hooks.callHook('my:hook', data).catch(err => { ... })
+ try { await nuxtApp.hooks.callHook('my:hook', data) } catch (err) { ... }
future.compatibilityVersion: 5 (см. Тестирование Nuxt 5) или явно experimental.asyncCallHook: false.Либо можно заставить callHook всегда возвращать Promise:
export default defineNuxtConfig({
experimental: {
asyncCallHook: true,
},
})
Плейсхолдеры клиентских компонентов в виде комментариев
🚦 Уровень влияния: Минимальный
Что изменилось
При compatibilityVersion: 5 клиентские компоненты (файлы .client.vue и обёртки createClientOnly()) на сервере рендерят HTML-комментарий (<!--placeholder-->) вместо пустого <div>.
Зачем это сделано
Если плейсхолдер <div> и корень компонента — один и тот же тег, рантайм Vue при гидратации может не пере-применить setScopeId, из-за чего пропадают scoped-стили после монтирования. Узел-комментарий устраняет конфликт имён тегов.
Шаги миграции
Если вы рассчитывали, что плейсхолдер <div> унаследует атрибуты (class, style и т.д.) для вёрстки (например, чтобы избежать сдвига), оберните компонент в <ClientOnly> со слотом #fallback:
- <MyComponent class="placeholder" style="min-height: 200px" />
+ <ClientOnly>
+ <MyComponent />
+ <template #fallback>
+ <div class="placeholder" style="min-height: 200px"></div>
+ </template>
+ </ClientOnly>
future.compatibilityVersion: 5 (см. Тестирование Nuxt 5) или явно experimental.clientNodePlaceholder: true.Либо вернуть прежнее поведение с плейсхолдером <div>:
export default defineNuxtConfig({
experimental: {
clientNodePlaceholder: false,
},
})