Guía de Actualización
Aprende cómo actualizar a la última versión de Nuxt.
Actualizando Nuxt
Última versión
Para actualizar Nuxt a la última versión, utiliza el comando nuxt upgrade
.
npx nuxt upgrade
Canal de Lanzamiento Nocturno
Para usar la última compilación de Nuxt y probar características antes de su lanzamiento, lee la guía sobre el canal de lanzamiento nocturno.
La etiqueta latest
del canal de lanzamiento nocturno actualmente sigue la rama Nuxt v4, lo que significa que es particularmente probable que tenga cambios importantes en este momento — ¡ten cuidado! Puedes optar por las versiones nocturnas de la rama 3.x con "nuxt": "npm:nuxt-nightly@3x"
.
Probando Nuxt 4
Nuxt 4 está programado para lanzarse en el segundo trimestre de 2025. Incluirá todas las características actualmente disponibles a través de compatibilityVersion: 4
.
Hasta el lanzamiento, es posible probar muchos de los cambios importantes de Nuxt 4 desde la versión 3.12+ de Nuxt.
Optando por Nuxt 4
Primero, actualiza Nuxt a la última versión.
Luego puedes configurar tu compatibilityVersion
para que coincida con el comportamiento de Nuxt 4:
export default defineNuxtConfig({
future: {
compatibilityVersion: 4,
},
// Para volver a habilitar _todo_ el comportamiento de Nuxt v3, configura las siguientes opciones:
// srcDir: '.',
// dir: {
// app: 'app'
// },
// experimental: {
// scanPageMeta: 'after-resolve',
// sharedPrerenderData: false,
// compileTemplate: true,
// resetAsyncDataToUndefined: true,
// templateUtils: true,
// relativeWatchPaths: true,
// normalizeComponentNames: false,
// spaLoadingTemplateLocation: 'within',
// parseErrorData: false,
// pendingWhenIdle: true,
// alwaysRunFetchOnKeyChange: true,
// defaults: {
// useAsyncData: {
// deep: true
// }
// }
// },
// features: {
// inlineStyles: true
// },
// unhead: {
// renderSSRHeadOptions: {
// omitLineBreaks: false
// }
// }
})
Por ahora, necesitas definir la versión de compatibilidad en cada capa que opte por el comportamiento de Nuxt 4. Esto no será necesario después del lanzamiento de Nuxt 4.
Cuando configuras tu compatibilityVersion
a 4
, los valores predeterminados en toda tu configuración de Nuxt cambiarán para optar por el comportamiento de Nuxt v4, pero puedes volver a habilitar granularmente el comportamiento de Nuxt v3 al probar, siguiendo las líneas comentadas arriba. Por favor, presenta problemas si es así, para que podamos abordarlos en Nuxt o en el ecosistema.
Los cambios importantes o significativos se anotarán aquí junto con los pasos de migración para la compatibilidad hacia atrás/adelante.
Esta sección está sujeta a cambios hasta el lanzamiento final, así que por favor revisa aquí regularmente si estás probando Nuxt 4 usando compatibilityVersion: 4
.
Migrando Usando Codemods
Para facilitar el proceso de actualización, hemos colaborado con el equipo de Codemod para automatizar muchos pasos de migración con algunos codemods de código abierto.
Si encuentras algún problema, por favor repórtalo al equipo de Codemod con npx codemod feedback
🙏
Para una lista completa de codemods de Nuxt 4, información detallada sobre cada uno, su fuente y varias formas de ejecutarlos, visita el Registro de Codemod.
Puedes ejecutar todos los codemods mencionados en esta guía usando la siguiente receta de codemod
:
npx codemod@latest nuxt/4/migration-recipe
Este comando ejecutará todos los codemods en secuencia, con la opción de deseleccionar cualquiera que no desees ejecutar. Cada codemod también se enumera a continuación junto con su respectivo cambio y puede ejecutarse de forma independiente.
Nueva Estructura de Directorios
🚦 Nivel de Impacto: Significativo
Nuxt ahora tiene por defecto una nueva estructura de directorios, con compatibilidad hacia atrás (por lo que si Nuxt detecta que estás usando la estructura antigua, como con un directorio pages/
de nivel superior, esta nueva estructura no se aplicará).
Qué Cambió
- el nuevo
srcDir
predeterminado de Nuxt esapp/
por defecto, y la mayoría de las cosas se resuelven desde allí. serverDir
ahora tiene por defecto<rootDir>/server
en lugar de<srcDir>/server
layers/
,modules/
ypublic/
se resuelven de forma relativa a<rootDir>
por defecto- si usas Nuxt Content v2.13+,
content/
se resuelve de forma relativa a<rootDir>
- se añade un nuevo
dir.app
, que es el directorio donde buscamosrouter.options.ts
yspa-loading-template.html
- esto tiene por defecto<srcDir>/
Un ejemplo de estructura de carpetas v4.
.output/
.nuxt/
app/
assets/
components/
composables/
layouts/
middleware/
pages/
plugins/
utils/
app.config.ts
app.vue
router.options.ts
content/
layers/
modules/
node_modules/
public/
server/
api/
middleware/
plugins/
routes/
utils/
nuxt.config.ts
👉 Para más detalles, consulta el PR que implementa este cambio.
Razones para el Cambio
- Rendimiento - colocar todo tu código en la raíz de tu repositorio causa problemas con las carpetas
.git/
ynode_modules/
siendo escaneadas/incluidas por los observadores de FS, lo que puede retrasar significativamente el inicio en sistemas que no son Mac OS. - Seguridad de tipo en IDE -
server/
y el resto de tu aplicación se ejecutan en dos contextos completamente diferentes con diferentes importaciones globales disponibles, y asegurarse de queserver/
no esté dentro de la misma carpeta que el resto de tu aplicación es un gran primer paso para garantizar que obtengas buenas autocompletaciones en tu IDE.
Pasos de Migración
- Crea un nuevo directorio llamado
app/
. - Mueve tus carpetas
assets/
,components/
,composables/
,layouts/
,middleware/
,pages/
,plugins/
yutils/
debajo de él, así comoapp.vue
,error.vue
,app.config.ts
. Si tienes unapp/router-options.ts
oapp/spa-loading-template.html
, estas rutas permanecen igual. - Asegúrate de que tus carpetas
nuxt.config.ts
,content/
,layers/
,modules/
,public/
yserver/
permanezcan fuera de la carpetaapp/
, en la raíz de tu proyecto. - Recuerda actualizar cualquier archivo de configuración de terceros para trabajar con la nueva estructura de directorios, como tu configuración de
tailwindcss
oeslint
(si es necesario -@nuxtjs/tailwindcss
debería configurar automáticamentetailwindcss
correctamente).
Puedes automatizar esta migración ejecutando npx codemod@latest nuxt/4/file-structure
Sin embargo, la migración no es necesaria. Si deseas mantener tu estructura de carpetas actual, Nuxt debería detectarla automáticamente. (Si no lo hace, por favor presenta un problema.) La única excepción es que si ya tienes un srcDir
personalizado. En este caso, debes tener en cuenta que tus carpetas modules/
, public/
y server/
se resolverán desde tu rootDir
en lugar de desde tu srcDir
personalizado. Puedes anular esto configurando dir.modules
, dir.public
y serverDir
si lo necesitas.
También puedes forzar una estructura de carpetas v3 con la siguiente configuración:
export default defineNuxtConfig({
// Esto revierte el nuevo valor predeterminado de srcDir de `app` a tu directorio raíz
srcDir: '.',
// Esto especifica el prefijo de directorio para `app/router.options.ts` y `app/spa-loading-template.html`
dir: {
app: 'app'
}
})
Capa de Obtención de Datos Singleton
🚦 Nivel de Impacto: Moderado
Qué Cambió
El sistema de obtención de datos de Nuxt (useAsyncData
y useFetch
) ha sido reorganizado significativamente para mejorar el rendimiento y la consistencia:
-
Referencias compartidas para la misma clave: Todas las llamadas a
useAsyncData
ouseFetch
con la misma clave ahora comparten las mismas referenciasdata
,error
ystatus
. Esto significa que es importante que todas las llamadas con una clave explícita no tengan opcionesdeep
,transform
,pick
,getCachedData
odefault
en conflicto. -
Más control sobre
getCachedData
: La funcióngetCachedData
ahora se llama cada vez que se obtienen datos, incluso si esto es causado por un observador o al llamar arefreshNuxtData
. (Anteriormente, siempre se obtenían nuevos datos y esta función no se llamaba en estos casos.) Para permitir más control sobre cuándo usar datos en caché y cuándo volver a obtenerlos, la función ahora recibe un objeto de contexto con la causa de la solicitud. -
Soporte para claves reactivas: Ahora puedes usar referencias computadas, referencias simples o funciones getter como claves, lo que permite la obtención automática de datos (y almacena los datos por separado).
-
Limpieza de datos: Cuando se desmonta el último componente que usa datos obtenidos con
useAsyncData
, Nuxt eliminará esos datos para evitar un uso de memoria en constante crecimiento.
Razones para el Cambio
Estos cambios se han realizado para mejorar el uso de memoria y aumentar la consistencia con los estados de carga a través de las llamadas de useAsyncData
.
Pasos de Migración
-
Verifica opciones inconsistentes: Revisa cualquier componente que use la misma clave con diferentes opciones o funciones de obtención.
// Esto ahora activará una advertencia const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false }) const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
Puede ser beneficioso extraer cualquier llamada a
useAsyncData
que comparta una clave explícita (y tenga opciones personalizadas) en su propio composable:composables/useUserData.tsexport function useUserData(userId: string) { return useAsyncData( `user-${userId}`, () => fetchUser(userId), { deep: true, transform: (user) => ({ ...user, lastAccessed: new Date() }) } ) }
-
Actualiza las implementaciones de
getCachedData
:useAsyncData('key', fetchFunction, { - getCachedData: (key, nuxtApp) => { - return cachedData[key] - } + getCachedData: (key, nuxtApp, ctx) => { + // ctx.cause - puede ser 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch' + + // Ejemplo: No usar caché en actualización manual + if (ctx.cause === 'refresh:manual') return undefined + + return cachedData[key] + } })
Alternativamente, por ahora, puedes desactivar este comportamiento con:
export default defineNuxtConfig({
experimental: {
granularCachedData: false,
purgeCachedData: false
}
})
Deducción de Metadatos de Ruta
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Es posible establecer algunos metadatos de ruta usando definePageMeta
, como el name
, path
, etc. Anteriormente, estos estaban disponibles tanto en la ruta como en los metadatos de la ruta (por ejemplo, route.name
y route.meta.name
).
Ahora, solo son accesibles en el objeto de ruta.
Razones para el Cambio
Esto es el resultado de habilitar experimental.scanPageMeta
por defecto, y es una optimización de rendimiento.
Pasos de Migración
La migración debería ser sencilla:
const route = useRoute()
- console.log(route.meta.name)
+ console.log(route.name)
Nombres de Componentes Normalizados
🚦 Nivel de Impacto: Moderado
Vue ahora generará nombres de componentes que coinciden con el patrón de nomenclatura de componentes de Nuxt.
Qué Cambió
Por defecto, si no lo has configurado manualmente, Vue asignará un nombre de componente que coincida con el nombre del archivo del componente.
├─ components/
├─── SomeFolder/
├───── MyComponent.vue
En este caso, el nombre del componente sería MyComponent
, en lo que respecta a Vue. Si quisieras usar <KeepAlive>
con él, o identificarlo en las DevTools de Vue, necesitarías usar este nombre.
Pero para auto-importarlo, necesitarías usar SomeFolderMyComponent
.
Con este cambio, estos dos valores coincidirán, y Vue generará un nombre de componente que coincida con el patrón de nomenclatura de componentes de Nuxt.
Pasos de Migración
Asegúrate de usar el nombre actualizado en cualquier prueba que use findComponent
de @vue/test-utils
y en cualquier <KeepAlive>
que dependa del nombre de tu componente.
Alternativamente, por ahora, puedes desactivar este comportamiento con:
export default defineNuxtConfig({
experimental: {
normalizeComponentNames: false
}
})
Unhead v2
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Unhead, utilizado para generar etiquetas <head>
, se ha actualizado a la versión 2. Aunque es mayormente compatible, incluye varios cambios importantes para las API de bajo nivel.
- Props eliminadas:
vmid
,hid
,children
,body
. - La entrada de Promise ya no es compatible.
- Las etiquetas ahora se ordenan usando Capo.js por defecto.
Pasos de Migración
Los cambios anteriores deberían tener un impacto mínimo en tu aplicación.
Si tienes problemas, deberías verificar:
- No estás usando ninguna de las props eliminadas.
useHead({
meta: [{
name: 'description',
// las etiquetas meta no necesitan un vmid, o una clave
- vmid: 'description'
- hid: 'description'
}]
})
- Si estás usando Template Params o Alias Tag Sorting, ahora necesitarás optar explícitamente por estas características.
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'
export default defineNuxtPlugin({
setup() {
const unhead = injectHead()
unhead.use(TemplateParamsPlugin)
unhead.use(AliasSortingPlugin)
}
})
Aunque no es necesario, se recomienda actualizar cualquier importación de @unhead/vue
a #imports
o nuxt/app
.
-import { useHead } from '@unhead/vue'
+import { useHead } from '#imports'
Si aún tienes problemas, puedes volver al comportamiento de la v1 habilitando la configuración head.legacy
.
export default defineNuxtConfig({
unhead: {
legacy: true,
}
})
Nueva Ubicación del DOM para la Pantalla de Carga de SPA
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Al renderizar una página solo para cliente (con ssr: false
), opcionalmente renderizamos una pantalla de carga (desde app/spa-loading-template.html
), dentro de la raíz de la aplicación Nuxt:
<div id="__nuxt">
<!-- plantilla de carga de spa -->
</div>
Ahora, por defecto, renderizamos la plantilla junto a la raíz de la aplicación Nuxt:
<div id="__nuxt"></div>
<!-- plantilla de carga de spa -->
Razones para el Cambio
Esto permite que la plantilla de carga de spa permanezca en el DOM hasta que se resuelva la suspensión de la aplicación Vue, evitando un destello de blanco.
Pasos de Migración
Si estabas apuntando a la plantilla de carga de spa con CSS o document.queryElement
, necesitarás actualizar tus selectores. Para este propósito, puedes usar las nuevas opciones de configuración app.spaLoaderTag
y app.spaLoaderAttrs
.
Alternativamente, puedes volver al comportamiento anterior con:
export default defineNuxtConfig({
experimental: {
spaLoadingTemplateLocation: 'within',
}
})
Datos de error.data
Analizados
🚦 Nivel de Impacto: Mínimo
Era posible lanzar un error con una propiedad data
, pero esta no se analizaba. Ahora, se analiza y está disponible en el objeto error
. Aunque es una corrección, esto es técnicamente un cambio importante si dependías del comportamiento anterior y lo analizabas manualmente.
Pasos de Migración
Actualiza tu error.vue
personalizado para eliminar cualquier análisis adicional de error.data
:
<script setup lang="ts">
import type { NuxtError } from '#app'
const props = defineProps({
error: Object as () => NuxtError
})
- const data = JSON.parse(error.data)
+ const data = error.data
</script>
Alternativamente, puedes desactivar este cambio:
export default defineNuxtConfig({
experimental: {
parseErrorData: false
},
})
Estilos en Línea Más Granulares
🚦 Nivel de Impacto: Moderado
Nuxt ahora solo incluirá estilos en línea para componentes Vue, no CSS global.
Qué Cambió
Anteriormente, Nuxt incluía todo el CSS, incluidos los estilos globales, y eliminaba los elementos <link>
a archivos CSS separados. Ahora, Nuxt solo hará esto para componentes Vue (que anteriormente producían fragmentos separados de CSS). Creemos que este es un mejor equilibrio para reducir las solicitudes de red separadas (como antes, no habrá solicitudes separadas para archivos .css
individuales por página o por componente en la carga inicial), así como permitir el almacenamiento en caché de un solo archivo CSS global y reducir el tamaño de descarga del documento de la solicitud inicial.
Pasos de Migración
Esta característica es completamente configurable y puedes volver al comportamiento anterior configurando inlineStyles: true
para incluir CSS global así como CSS por componente.
export default defineNuxtConfig({
features: {
inlineStyles: true
}
})
Escaneo de Metadatos de Página Después de la Resolución
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Ahora escaneamos los metadatos de la página (definidos en definePageMeta
) después de llamar al hook pages:extend
en lugar de antes.
Razones para el Cambio
Esto fue para permitir el escaneo de metadatos para páginas que los usuarios querían agregar en pages:extend
. Todavía ofrecemos una oportunidad para cambiar o anular los metadatos de la página en un nuevo hook pages:resolved
.
Pasos de Migración
Si deseas anular los metadatos de la página, hazlo en pages:resolved
en lugar de en pages:extend
.
export default defineNuxtConfig({
hooks: {
- 'pages:extend'(pages) {
+ 'pages:resolved'(pages) {
const myPage = pages.find(page => page.path === '/')
myPage.meta ||= {}
myPage.meta.layout = 'overridden-layout'
}
}
})
Alternativamente, puedes volver al comportamiento anterior con:
export default defineNuxtConfig({
experimental: {
scanPageMeta: true
}
})
Datos de Prerender Compartidos
🚦 Nivel de Impacto: Medio
Qué Cambió
Habilitamos una característica experimental previamente para compartir datos de llamadas useAsyncData
y useFetch
, a través de diferentes páginas. Ver PR original.
Razones para el Cambio
Esta característica comparte automáticamente los datos de carga entre páginas que son prerenderizadas. Esto puede resultar en una mejora significativa del rendimiento al prerenderizar sitios que usan useAsyncData
o useFetch
y obtienen los mismos datos en diferentes páginas.
Por ejemplo, si tu sitio requiere una llamada useFetch
para cada página (por ejemplo, para obtener datos de navegación para un menú, o configuraciones del sitio desde un CMS), estos datos solo se obtendrían una vez al prerenderizar la primera página que los usa, y luego se almacenarían en caché para su uso al prerenderizar otras páginas.
Pasos de Migración
Asegúrate de que cualquier clave única de tus datos siempre sea resoluble a los mismos datos. Por ejemplo, si estás usando useAsyncData
para obtener datos relacionados con una página en particular, deberías proporcionar una clave que coincida de manera única con esos datos. (useFetch
debería hacer esto automáticamente por ti.)
// Esto sería inseguro en una página dinámica (por ejemplo, `[slug].vue`) porque el slug de la ruta hace una diferencia
// en los datos obtenidos, pero Nuxt no puede saberlo porque no está reflejado en la clave.
const route = useRoute()
const { data } = await useAsyncData(async () => {
return await $fetch(`/api/my-page/${route.params.slug}`)
})
// En su lugar, deberías usar una clave que identifique de manera única los datos obtenidos.
const { data } = await useAsyncData(route.params.slug, async () => {
return await $fetch(`/api/my-page/${route.params.slug}`)
})
Alternativamente, puedes desactivar esta característica con:
export default defineNuxtConfig({
experimental: {
sharedPrerenderData: false
}
})
Valores Predeterminados de data
y error
en useAsyncData
y useFetch
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Los objetos data
y error
devueltos de useAsyncData
ahora tendrán por defecto undefined
.
Razones para el Cambio
Anteriormente, data
se inicializaba en null
pero se restablecía en clearNuxtData
a undefined
. error
se inicializaba en null
. Este cambio es para lograr una mayor consistencia.
Pasos de Migración
Si estabas verificando si data.value
o error.value
eran null
, puedes actualizar estas verificaciones para verificar undefined
en su lugar.
Puedes automatizar este paso ejecutando npx codemod@latest nuxt/4/default-data-error-value
Si encuentras algún problema, puedes volver al comportamiento anterior con:
export default defineNuxtConfig({
experimental: {
defaults: {
useAsyncData: {
value: 'null',
errorValue: 'null'
}
}
}
})
Por favor, presenta un problema si estás haciendo esto, ya que no planeamos mantener esto como configurable.
Eliminación de valores boolean
obsoletos para la opción dedupe
al llamar a refresh
en useAsyncData
y useFetch
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Anteriormente era posible pasar dedupe: boolean
a refresh
. Estos eran alias de cancel
(true
) y defer
(false
).
const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt!' }))
async function refreshData () {
await refresh({ dedupe: true })
}
Razones para el Cambio
Estos alias fueron eliminados, para mayor claridad.
El problema surgió al agregar dedupe
como una opción a useAsyncData
, y eliminamos los valores booleanos ya que terminaron siendo opuestos.
refresh({ dedupe: false })
significaba no cancelar solicitudes existentes a favor de esta nueva. Pero pasar dedupe: true
dentro de las opciones de useAsyncData
significa no hacer nuevas solicitudes si hay una solicitud pendiente existente. (Ver PR.)
Pasos de Migración
La migración debería ser sencilla:
const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' }))
async function refreshData () {
- await refresh({ dedupe: true })
+ await refresh({ dedupe: 'cancel' })
- await refresh({ dedupe: false })
+ await refresh({ dedupe: 'defer' })
}
Puedes automatizar este paso ejecutando npx codemod@latest nuxt/4/deprecated-dedupe-value
Respeto a los valores predeterminados al borrar data
en useAsyncData
y useFetch
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Si proporcionas un valor default
personalizado para useAsyncData
, ahora se usará al llamar a clear
o clearNuxtData
y se restablecerá a su valor predeterminado en lugar de simplemente desasignarse.
Razones para el Cambio
A menudo, los usuarios establecen un valor vacío apropiado, como un array vacío, para evitar la necesidad de verificar null
/undefined
al iterar sobre él. Esto debería respetarse al restablecer/borrar los datos.
Pasos de Migración
Si encuentras algún problema, puedes volver al comportamiento anterior, por ahora, con:
export default defineNuxtConfig({
experimental: {
resetAsyncDataToUndefined: true,
}
})
Por favor, presenta un problema si estás haciendo esto, ya que no planeamos mantener esto como configurable.
Alineación del valor pending
en useAsyncData
y useFetch
🚦 Nivel de Impacto: Medio
El objeto pending
devuelto de useAsyncData
, useFetch
, useLazyAsyncData
y useLazyFetch
ahora es una propiedad computada que es true
solo cuando status
también está pendiente.
Qué Cambió
Ahora, cuando se pasa immediate: false
, pending
será false
hasta que se realice la primera solicitud. Esto es un cambio del comportamiento anterior, donde pending
siempre era true
hasta que se realizaba la primera solicitud.
Razones para el Cambio
Esto alinea el significado de pending
con la propiedad status
, que también está pendiente cuando la solicitud está en progreso.
Pasos de Migración
Si dependes de la propiedad pending
, asegúrate de que tu lógica tenga en cuenta el nuevo comportamiento donde pending
solo será true
cuando el estado también esté pendiente.
<template>
- <div v-if="!pending">
+ <div v-if="status === 'success'">
<p>Data: {{ data }}</p>
</div>
<div v-else>
<p>Loading...</p>
</div>
</template>
<script setup lang="ts">
const { data, pending, execute, status } = await useAsyncData(() => fetch('/api/data'), {
immediate: false
})
onMounted(() => execute())
</script>
Alternativamente, puedes revertir temporalmente al comportamiento anterior con:
export default defineNuxtConfig({
experimental: {
pendingWhenIdle: true
}
})
Comportamiento de Cambio de Clave en useAsyncData
y useFetch
🚦 Nivel de Impacto: Medio
Qué Cambió
Al usar claves reactivas en useAsyncData
o useFetch
, Nuxt vuelve a obtener datos automáticamente cuando cambia la clave. Cuando se establece immediate: false
, useAsyncData
solo obtendrá datos cuando cambie la clave si los datos ya se han obtenido una vez.
Anteriormente, useFetch
tenía un comportamiento ligeramente diferente. Siempre obtenía datos cuando cambiaba la clave.
Ahora, useFetch
y useAsyncData
se comportan de manera consistente, solo obteniendo datos cuando cambia la clave si los datos ya se han obtenido una vez.
Razones para el Cambio
Esto asegura un comportamiento consistente entre useAsyncData
y useFetch
, y previene obtenciones inesperadas. Si has configurado immediate: false
, entonces debes llamar a refresh
o execute
o los datos nunca se obtendrán en useFetch
o useAsyncData
.
Pasos de Migración
Este cambio debería mejorar generalmente el comportamiento esperado, pero si esperabas que cambiar la clave o las opciones de un useFetch
no inmediato, ahora necesitarás activarlo manualmente la primera vez.
const id = ref('123')
const { data, execute } = await useFetch('/api/test', {
query: { id },
immediate: false
)
+ watch(id, execute, { once: true })
Para optar por no participar en este comportamiento:
// O globalmente en tu configuración de Nuxt
export default defineNuxtConfig({
experimental: {
alwaysRunFetchOnKeyChange: true
}
})
Reactividad de Datos Superficial en useAsyncData
y useFetch
🚦 Nivel de Impacto: Mínimo
El objeto data
devuelto de useAsyncData
, useFetch
, useLazyAsyncData
y useLazyFetch
ahora es un shallowRef
en lugar de un ref
.
Qué Cambió
Cuando se obtienen nuevos datos, cualquier cosa que dependa de data
seguirá siendo reactiva porque se reemplaza todo el objeto. Pero si tu código cambia una propiedad dentro de esa estructura de datos, esto no activará ninguna reactividad en tu aplicación.
Razones para el Cambio
Esto trae una mejora significativa en el rendimiento para objetos y arrays profundamente anidados porque Vue no necesita observar cada propiedad/array para modificaciones. En la mayoría de los casos, data
también debería ser inmutable.
Pasos de Migración
En la mayoría de los casos, no se requieren pasos de migración, pero si dependes de la reactividad del objeto de datos, entonces tienes dos opciones:
- Puedes optar granularmente por la reactividad profunda en una base por composable:
- const { data } = useFetch('/api/test') + const { data } = useFetch('/api/test', { deep: true })
- Puedes cambiar el comportamiento predeterminado en todo el proyecto (no recomendado):
nuxt.config.ts
export default defineNuxtConfig({ experimental: { defaults: { useAsyncData: { deep: true } } } })
Si necesitas, puedes automatizar este paso ejecutando npx codemod@latest nuxt/4/shallow-function-reactivity
Rutas de Observación Absolutas en builder:watch
🚦 Nivel de Impacto: Mínimo
Qué Cambió
El hook builder:watch
de Nuxt ahora emite una ruta que es absoluta en lugar de relativa a tu srcDir
del proyecto.
Razones para el Cambio
Esto nos permite admitir la observación de rutas que están fuera de tu srcDir
, y ofrece un mejor soporte para capas y otros patrones más complejos.
Pasos de Migración
Ya hemos migrado proactivamente los módulos públicos de Nuxt que sabemos que usan este hook. Ver issue #25339.
Sin embargo, si eres un autor de módulos que usa el hook builder:watch
y deseas mantener la compatibilidad hacia atrás/adelante, puedes usar el siguiente código para asegurarte de que tu código funcione igual en Nuxt v3 y Nuxt v4:
+ import { relative, resolve } from 'node:fs'
// ...
nuxt.hook('builder:watch', async (event, path) => {
+ path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path))
// ...
})
Puedes automatizar este paso ejecutando npx codemod@latest nuxt/4/absolute-watch-path
Eliminación del objeto window.__NUXT__
Qué Cambió
Estamos eliminando el objeto global window.__NUXT__
después de que la aplicación termine la hidratación.
Razones para el Cambio
Esto abre el camino a patrones de múltiples aplicaciones (#21635) y nos permite centrarnos en una única forma de acceder a los datos de la aplicación Nuxt - useNuxtApp()
.
Pasos de Migración
Los datos aún están disponibles, pero se pueden acceder con useNuxtApp().payload
:
- console.log(window.__NUXT__)
+ console.log(useNuxtApp().payload)
Escaneo de Índice de Directorios
🚦 Nivel de Impacto: Medio
Qué Cambió
Las carpetas secundarias en tu carpeta middleware/
también se escanean en busca de archivos index
y ahora también se registran como middleware en tu proyecto.
Razones para el Cambio
Nuxt escanea automáticamente una serie de carpetas, incluidas middleware/
y plugins/
.
Las carpetas secundarias en tu carpeta plugins/
se escanean en busca de archivos index
y queríamos hacer que este comportamiento fuera consistente entre los directorios escaneados.
Pasos de Migración
Probablemente no sea necesaria ninguna migración, pero si deseas volver al comportamiento anterior, puedes agregar un hook para filtrar estos middleware:
export default defineNuxtConfig({
hooks: {
'app:resolve'(app) {
app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path))
}
}
})
Cambios en la Compilación de Plantillas
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Anteriormente, Nuxt usaba lodash/template
para compilar plantillas ubicadas en el sistema de archivos usando el formato/sintaxis de archivo .ejs
.
Además, proporcionamos algunas utilidades de plantilla (serialize
, importName
, importSources
) que podrían usarse para la generación de código dentro de estas plantillas, que ahora se están eliminando.
Razones para el Cambio
En Nuxt v3 nos movimos a una sintaxis 'virtual' con una función getContents()
que es mucho más flexible y eficiente.
Además, lodash/template
ha tenido una sucesión de problemas de seguridad. Estos realmente no se aplican a los proyectos de Nuxt porque se está utilizando en tiempo de compilación, no en tiempo de ejecución, y por código de confianza. Sin embargo, aún aparecen en auditorías de seguridad. Además, lodash
es una dependencia pesada y no es utilizada por la mayoría de los proyectos.
Finalmente, proporcionar funciones de serialización de código directamente dentro de Nuxt no es ideal. En su lugar, mantenemos proyectos como unjs/knitwork que pueden ser dependencias de tu proyecto, y donde los problemas de seguridad pueden ser reportados/resueltos directamente sin requerir una actualización de Nuxt en sí.
Pasos de Migración
Hemos presentado PRs para actualizar módulos que usan sintaxis EJS, pero si necesitas hacer esto tú mismo, tienes tres alternativas compatibles hacia atrás/adelante:
- Mover tu lógica de interpolación de cadenas directamente a
getContents()
. - Usar una función personalizada para manejar el reemplazo, como en https://github.com/nuxt-modules/color-mode/pull/240.
- Usar
es-toolkit/compat
(un reemplazo directo para lodash template), como una dependencia de tu proyecto en lugar de Nuxt:
+ import { readFileSync } from 'node:fs'
+ import { template } from 'es-toolkit/compat'
// ...
addTemplate({
fileName: 'appinsights-vue.js'
options: { /* algunas opciones */ },
- src: resolver.resolve('./runtime/plugin.ejs'),
+ getContents({ options }) {
+ const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8')
+ return template(contents)({ options })
+ },
})
Finalmente, si estás usando las utilidades de plantilla (serialize
, importName
, importSources
), puedes reemplazarlas de la siguiente manera con utilidades de knitwork
:
import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork'
const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1'))
const importSources = (sources: string | string[], { lazy = false } = {}) => {
return toArray(sources).map((src) => {
if (lazy) {
return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}`
}
return genImport(src, genSafeVariableName(src))
}).join('\n')
}
const importName = genSafeVariableName
Puedes automatizar este paso ejecutando npx codemod@latest nuxt/4/template-compilation-changes
Eliminación de Características Experimentales
🚦 Nivel de Impacto: Mínimo
Qué Cambió
Cuatro características experimentales ya no son configurables en Nuxt 4:
experimental.treeshakeClientOnly
serátrue
(predeterminado desde v3.0)experimental.configSchema
serátrue
(predeterminado desde v3.3)experimental.polyfillVueUseHead
seráfalse
(predeterminado desde v3.4)experimental.respectNoSSRHeader
seráfalse
(predeterminado desde v3.4)vite.devBundler
ya no es configurable - usarávite-node
por defecto
Razones para el Cambio
Estas opciones han estado configuradas en sus valores actuales durante algún tiempo y no tenemos razones para creer que necesiten seguir siendo configurables.
Pasos de Migración
-
polyfillVueUseHead
es implementable en el lado del usuario con este plugin -
respectNoSSRHeader
es implementable en el lado del usuario con middleware de servidor
Nuxt 2 vs. Nuxt 3+
En la tabla a continuación, hay una comparación rápida entre 3 versiones de Nuxt:
Característica / Versión | Nuxt 2 | Nuxt Bridge | Nuxt 3+ |
---|---|---|---|
Vue | 2 | 2 | 3 |
Estabilidad | 😊 Estable | 😊 Estable | 😊 Estable |
Rendimiento | 🏎 Rápido | ✈️ Más rápido | 🚀 Más rápido |
Motor Nitro | ❌ | ✅ | ✅ |
Soporte ESM | 🌙 Parcial | 👍 Mejor | ✅ |
TypeScript | ☑️ Opt-in | 🚧 Parcial | ✅ |
API de Composición | ❌ | 🚧 Parcial | ✅ |
API de Opciones | ✅ | ✅ | ✅ |
Importación Automática de Componentes | ✅ | ✅ | ✅ |
Sintaxis <script setup> | ❌ | 🚧 Parcial | ✅ |
Importaciones Automáticas | ❌ | ✅ | ✅ |
webpack | 4 | 4 | 5 |
Vite | ⚠️ Parcial | 🚧 Parcial | ✅ |
CLI de Nuxt | ❌ Antiguo | ✅ nuxt | ✅ nuxt |
Sitios estáticos | ✅ | ✅ | ✅ |
Nuxt 2 a Nuxt 3+
La guía de migración proporciona una comparación paso a paso de las características de Nuxt 2 a las características de Nuxt 3+ y orientación para adaptar tu aplicación actual.
Ver también migration > overviewNuxt 2 a Nuxt Bridge
Si prefieres migrar progresivamente tu aplicación Nuxt 2 a Nuxt 3, puedes usar Nuxt Bridge. Nuxt Bridge es una capa de compatibilidad que te permite usar características de Nuxt 3+ en Nuxt 2 con un mecanismo de opt-in.
Ver también bridge > overview※Esta página es una traducción no oficial de la documentación oficial de Nuxt.js.
La página correspondiente en la documentación oficial está aquí:
https://nuxt.com/docs/3.x/getting-started/upgrade