Привет, Хабр!
Меня зовут Иван и я занимаюсь frontend-разработкой в компании SimbirSoft. В марте 2026 на официальном сайте Nuxt объявили о выходе новой версии Nuxt v4.4. В ней произведена миграция на Vue Router v5, добавлена возможность создания пользовательских фабрик useFetch/useAsyncData, типизация пропсов в Layout, новый композабл и компоненты для работы с доступностью, профилирование сборки, улучшение производительности и многое другое.
Также в январе вместе с выходом версии Nuxt v4.3 разработчики продлили поддержку Nuxt 3 до 31 июля 2026 года (ранее указывали срок до 31 января 2026 года). Решение было принято после открытия обсуждения, чтобы узнать у сообщества, как прошло обновление с версии 3 до версии 4. Многие отметили, что ждут новостей о сроках релиза Nuxt 5, который должен выйти вместе с Nitro 3, поэтому пока не спешат обновляться на четвертую версию. А пока мы ждем новостей, давайте посмотрим, какие интересные нововведения появились в последних релизах Nuxt. Данная статья — это краткий обзор на новые фичи и изменения, которые мне показались интересными.
Дисклеймер: Далее по тексту я буду в скобках указывать версию Nuxt, начиная с которой, данная фича поддерживается, например (v4.4).
Миграция на Vue Router v5 (v4.4)
В конце января вышла новая версия Vue Router v5. В этой версии плагин unplugin-vue-router (маршрутизация на основе файлов) интегрирован в основной пакет. Команда Nuxt не стала долго ждать и провела миграцию на новую версию. Это первое мажорное обновление Vue Router со времён Nuxt 3. Если вы подключали unplugin-vue-router отдельно, то его можно убрать из зависимостей. Далее планируется вывести типизированные маршруты из экспериментального статуса.
Профилирование сборки (v4.4)
Добавлена возможность профилирования сборки для выявления узких мест с помощью команды: nuxt build --profile. Для удобства эту команду можно добавить в отдельный скрипт в файле package.json:
// package.json "scripts": { // … другие скрипты "profile": "nuxt build --profile=verbose", }
Дополнительный параметр verbose указывается для получения более подробной информации. Теперь профилирование можно запустить с помощью npm run profile.

В отчете указаны длительность, изменение постоянного потребления памяти RSS (Resident Set Size) и изменение Heap для каждого этапа сборки. Также предоставляется информация по отдельным модулям и плагинам сборщика. Например, благодаря этой информации можно с легкостью определить, какой из плагинов замедляет сборку приложения.
Записываются три формата вывода:
Chrome Trace (./node_modules/.cache/nuxt/.nuxt/perf-report.json)
JSON-отчёт (./node_modules/.cache/nuxt/.nuxt/perf-trace.json)
CPU-профиль (./nuxt-build.cpuprofile)
Добавление опции appLayout в правилах маршрутизации (v4.3)
// nuxt.config.ts export default defineNuxtConfig({ routeRules: { '/admin/**': { appLayout: 'admin' }, '/products/**': { appLayout: 'default' }, '/auth/**': { appLayout: 'minimal' } } })
Раньше приходилось устанавливать Layout на каждой странице или пользоваться промежуточными функциями и библиотеками для настройки в одном месте. Теперь мы можем задавать Layout непосредственно в правилах маршрутизации. Это обеспечивает централизованный способ управления во всем приложении без разбросанных вызовов definePageMeta по страницам.
Группы маршрутов в метаданных страницы (v4.3)
В некоторых случаях может потребоваться сгруппировать набор маршрутов таким образом, чтобы это не влияло на маршрутизацию на основе файлов. Для этой цели можно поместить файлы в папку, заключенную в скобки — создать группу маршрутов. Группа игнорируется при формировании структуры URL-адресов.
-| pages/ ---| index.vue ---| (protected)/ -----| dashboard.vue -----| admin.vue
Теперь эти данные отображаются в метаданных страницы. Это позволяет легко проверить, к каким группам принадлежит маршрут, и применить ту или иную логику, где у вас есть доступ к маршруту, например: в middleware или самом компоненте страницы.
// pages/(protected)/dashboard.vue <script setup lang="ts"> const route = useRoute(); </script> <template> <div> <p v-if="route.meta.groups?.includes('protected')"> This is a protected page </p> </div> </template> // middleware/auth.ts export default defineNuxtRouteMiddleware((to) => { if (to.meta.groups?.includes('protected') && !isAuthenticated()) { return navigateTo('/login'); } })
Прерывание процесса получения данных при запросах (v4.2)
Добавлена возможность использовать сигналы AbortController непосредственно внутри useAsyncData. Это работает путем передачи внутреннего сигнала процессу в useAsyncData для отмены любого промиса, предоставляемому самим Nuxt.
const { data, error, clear, refresh } = await useAsyncData( 'users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal, // передаем сигнал в $fetch }), ); // … refresh(); // фактически отменит запрос $fetch (если используется dedupe: cancel) // … clear(); // отменит последний ожидающий запрос
Также появилась возможность передавать сигналы AbortController непосредственно в refresh/execute, что обеспечивает точный контроль над отменой запросов. Это особенно полезно, когда необходимо прерывать запросы на основе действий пользователя или событий жизненного цикла компонента.
const { data, refresh } = await useAsyncData('posts', fetchPosts); // abortController в refresh функции const abortController = new AbortController(); refresh({ signal: abortController.signal }); // Далее в коде abortController.abort();
Улучшение Lazy Hydration (v4.1)
Начиная с версии Nuxt 3.16, была добавлена возможность оптимизировать приложения, используя ленивую гидратацию. Техника, при которой отдельные компоненты лениво подгружаются и становятся интерактивными при определенных условиях, например: при попадании в область видимости, различных действиях пользователя. Но это породило новую проблему в проектах с отключенной функцией автоимпортов. В них ленивая гидратация просто не работала, так как импорт компонентов происходит напрямую без предварительной обработки Lazy префиксов. Пример кода с неработающей ленивой гидратацией:
// nuxt.config.ts export default defineNuxtConfig({ components: false, imports: { autoImport: false, }, }) // index.vue <script setup lang="ts"> import LazyMyFooter from "../components/MyFooter.vue"; </script> <template> <div> <div style="height: 2000px"></div> <LazyMyFooter :hydrate-on-visible="{ rootMargin: '100px' }" /> </div> </template>
И наконец, начиная с версий 4.1/3.19, ��та проблема решена. Добавлен специальный макрос defineLazyHydrationComponent. Используем его, чтобы решить проблему, и перепишем код сверху.
// index.vue <script setup lang="ts"> import { defineLazyHydrationComponent } from "#imports"; const LazyMyFooter = defineLazyHydrationComponent( "visible", () => import("../components/MyFooter.vue"), ); </script> <template> <div> <div style="height: 2000px"></div> <LazyMyFooter :hydrate-on-visible="{ rootMargin: '100px' }" /> </div> </template>
Улучшения сборки и производительности (v4.1)
По умолчанию чанки, генерируемые при сборке Vite, хешируются и хеш добавляется к названию файлов, что позволяет кэшировать их. Возникает проблема, когда при обновлении одного небольшого компонента, происходит каскадное обновление хешей всех чанков при новой сборке. Мы не можем использовать повторно ни один из файлов, происходит сбрасывание кэша. Это серьезная проблема в производительности. Мы ожидаем, что хеш изменится только в изменяемых файлах. Почему это происходит? Давайте рассмотрим небольшую схему.

Структура небольшого проекта: две страницы index, about и компонент MyAbout, который импортируется в about.vue.
-| pages/ ---| index.vue ---| about.vue -| components/ -----| MyAbout.vue
В приложении Nuxt есть entry файл со своим хешем, который импортируется почти во все наши файлы и содержит импорты других файлов. Это связано с динамическими импортами, чтобы не подгружать все страницы сразу, а по мере необходимости. Изменяем компонент MyAbout, его хеш изменится. Далее по цепочке изменится хеш страницы about, а затем entry. Изменение хеша entry файла повлечёт изменение хеша index файла.
Добавление карты импорта в начало тега <head> решает данную проблему:
<script type="importmap">{"imports":{"#entry":"/_nuxt/DC5HVSK5.js"}}</script>
Теперь внутри чанков импорт будет осуществляться из #entry, а не напрямую. Поэтому изменения в entry не приведут к аннулированию хешей в файлах, которые остались неизменными.
В новых версиях Nuxt это поведение будет включено автоматически. Но могут возникнуть проблемы с поддержкой importmap в старых браузерах. В конфиге nuxt.config.ts можно отключить данное поведение:
// nuxt.config.ts export default defineNuxtConfig({ experimental: { entryImportMap: false, }, // или vite: { build: { target: 'safari13', }, }, })
Заключение
Подведём итог. В этой статье мы рассмотрели некоторые новые возможности Nuxt:
миграция на Vue Router v5;
профилирование сборки с помощью nuxt build --profile=verbose;
appLayout в правилах маршрутизации для централизованной настройки layout страниц;
группы маршрутов в метаданных страницы для объединения страниц и применения к ним особой логики;
AbortController для лучшего контроля запросов;
макрос defineLazyHydrationComponent для улучшения ленивой гидратации при отключенных авто импортах;
улучшения сборки и производительности.
Очень радует, что разработчики Nuxt прислушиваются к сообществу, постоянно добавляют новые фичи, стараются быстро исправлять баги, работают над производительностью и стабильностью фреймворка.
Обзор был сделан лишь на малую часть нового функционала. А какие фичи понравились вам в новых версиях? Мигрировали ли вы уже на Nuxt 4, какие проблемы были при миграции и ждёте ли вы Nuxt 5? Делитесь в комментариях.
Подписывайся на наши соцсети и блог, где мы публикуем другие полезные материалы, в том числе и для frotend-разработчиков:
