Saltar al contenido

Migrar desde NuxtJS

Aquí hay algunos conceptos clave y estrategias de migración para ayudarte a empezar. ¡Usa el resto de nuestra documentación y nuestra comunidad de Discord para seguir adelante!

Esta guía se refiere a Nuxt 2, no al más nuevo Nuxt 3. Aunque algunos de los conceptos son similares, Nuxt 3 es una versión más nueva del framework y puede requerir estrategias diferentes para partes de tu migración.

Nuxt y Astro comparten algunas similitudes que te ayudarán a migrar tu proyecto:

Cuando reconstruyas tu sitio de Nuxt en Astro, notarás algunas diferencias importantes:

  • Nuxt es una SPA (single-page application) basada en Vue. Los sitios de Astro son apps multi-página construidas usando componentes .astro, pero también pueden soportar React, Preact, Vue.js, Svelte, SolidJS, AlpineJS, y plantillas HTML crudo.

  • Enrutamiento de páginas: Nuxt usa vue-router para enrutamiento SPA, y vue-meta para gestionar <head>. En Astro, crearás rutas de páginas HTML separadas y controlarás el <head> de tu página directamente, o en un componente de layout.

  • orientado a contenido: Astro fue diseñado para mostrar tu contenido y permitirte optar por la interactividad solo cuando sea necesario. Una app existente de Nuxt puede estar construida para alta interactividad del lado del cliente. Astro tiene capacidades integradas para trabajar con tu contenido, como la generación de páginas, pero puede requerir técnicas avanzadas de Astro para incluir elementos que son más difíciles de replicar usando componentes .astro, como dashboards.

Cada migración de proyecto será diferente, pero hay algunas acciones comunes que realizarás al convertir de Nuxt a Astro.

Usa el comando create astro de tu gestor de paquetes para lanzar el asistente CLI de Astro o elige un tema de la comunidad del Astro Theme Showcase.

Puedes pasar un argumento --template al comando create astro para iniciar un nuevo proyecto de Astro con uno de nuestros starters oficiales (p. ej. docs, blog, portfolio). O, puedes iniciar un nuevo proyecto desde cualquier repositorio existente de Astro en GitHub.

Ventana de la terminal
# launch the Astro CLI Wizard
npm create astro@latest
# create a new project with an official example
npm create astro@latest -- --template <example-name>

Luego, copia los archivos existentes de tu proyecto de Nuxt a tu nuevo proyecto de Astro en una carpeta separada fuera de src.

Puede que te resulte útil instalar algunas de las integraciones opcionales de Astro para usar mientras conviertes tu proyecto de Nuxt a Astro:

  • @astrojs/vue: para reutilizar algunos componentes de UI de Vue existentes en tu nuevo sitio de Astro, o seguir escribiendo con componentes de Vue.

  • @astrojs/mdx: para traer archivos MDX existentes de tu proyecto de Nuxt, o para usar MDX en tu nuevo sitio de Astro.

  1. Mueve el contenido de la carpeta static/ de Nuxt a public/.

    Astro usa el directorio public/ para assets estáticos, similar a la carpeta static/ de Nuxt.

  2. Copia o mueve otros archivos y carpetas de Nuxt (p. ej. pages, layouts etc.) a la carpeta src/ de Astro.

    Como Nuxt, la carpeta src/pages/ de Astro es una carpeta especial usada para enrutamiento basado en archivos. Todas las demás carpetas son opcionales, y puedes organizar el contenido de tu carpeta src/ como quieras. Otras carpetas comunes en proyectos de Astro incluyen src/layouts/, src/components, src/styles, src/scripts.

Aquí hay algunos tips para convertir un componente .vue de Nuxt en un componente .astro:

  1. Usa el <template> de la función del componente existente de NuxtJS como base para tu plantilla HTML.

  2. Cambia cualquier sintaxis de Nuxt o Vue a sintaxis de Astro o a estándares web de HTML. Esto incluye <NuxtLink>, :class, {{variable}}, y v-if, por ejemplo.

  3. Mueve el JavaScript de <script> a un “code fence” (---). Convierte las propiedades de data-fetching de tu componente a JavaScript del lado del servidor - consulta Data fetching de Nuxt a Astro.

  4. Usa Astro.props para acceder a cualquier prop adicional que se pasara previamente a tu componente de Vue.

  5. Decide si algún componente importado también necesita convertirse a Astro. Con la integración oficial instalada, puedes usar componentes de Vue existentes en tu archivo de Astro. Pero, puede que quieras convertirlos a Astro, ¡especialmente si no necesitan ser interactivos!

Ve un ejemplo de una app Nuxt convertida paso a paso.

Compara el siguiente componente de Nuxt y un componente de Astro correspondiente:

Page.vue
<template>
<div>
<p v-if="message === 'Not found'">
The repository you're looking up doesn't exist
</p>
<div v-else>
<Header/>
<p class="banner">Astro has {{stars}} 🧑‍🚀</p>
<Footer />
</div>
</div>
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
name: 'IndexPage',
async asyncData() {
const res = await fetch('https://api.github.com/repos/withastro/astro')
const json = await res.json();
return {
message: json.message,
stars: json.stargazers_count || 0,
};
}
});
</script>
<style scoped>
.banner {
background-color: #f4f4f4;
padding: 1em 1.5em;
text-align: center;
margin-bottom: 1em;
}
</style>

Puede que te resulte útil comenzar convirtiendo tus layouts y plantillas de Nuxt en componentes de layout de Astro.

Cada página de Astro requiere explícitamente que las etiquetas <html>, <head>, y <body> estén presentes. Tu layout.vue y plantillas de Nuxt no incluirán estos.

Ten en cuenta las plantillas HTML estándar, y el acceso directo a <head>:

src/layouts/Layout.astro
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<title>Astro</title>
</head>
<body>
<!-- Wrap the slot element with your existing layout templating -->
<slot />
</body>
</html>

También puedes querer reutilizar código de la propiedad head de la página de Nuxt para incluir metadatos adicionales del sitio. Ten en cuenta que Astro no usa vue-meta ni la propiedad head de un componente sino que crea <head> directamente. Puedes importar y usar componentes, incluso dentro de <head>, para separar y organizar el contenido de tu página.

En NuxtJS, tus pages viven en /pages. En Astro, todo el contenido de tus páginas debe vivir dentro de src/ a menos que uses content collections.

Tus páginas Vue (.vue) de Nuxt existentes necesitarán ser convertidas de archivos Vue a páginas .astro. No puedes usar un archivo de página Vue existente en Astro.

Estas páginas .astro deben estar ubicadas dentro de src/pages/ y tendrán rutas de página generadas automáticamente basadas en su ruta de archivo.

En Nuxt, tus páginas dinámicas usan un guión bajo para representar una propiedad de página dinámica que luego se pasa a la generación de páginas:

  • Directoriopages/
    • Directorypokemon/
      • _name.vue
    • index.vue
  • nuxt.config.js

Para convertir a Astro, cambia esta propiedad de ruta dinámica con guión bajo (p. ej. _name.vue) para envolverla en un par de corchetes (p. ej. [name].astro):

  • Directoriosrc/
    • Directoriopages/
      • Directorypokemon/
        • [name].astro
      • index.astro
  • astro.config.mjs

Astro tiene soporte integrado para Markdown y una integración opcional para archivos MDX. Puedes reutilizar cualquier página Markdown y MDX existente, pero pueden requerir algunos ajustes en su frontmatter, como añadir la propiedad especial de frontmatter layout de Astro.

Ya no necesitarás crear páginas manualmente para cada ruta generada por Markdown o usar un paquete externo como @nuxt/content. Estos archivos pueden colocarse dentro de src/pages/ para aprovechar el enrutamiento automático basado en archivos.

Cuando son parte de una content collection, generarás páginas dinámicamente desde tus entradas de contenido.

Como Astro devuelve HTML crudo, es posible escribir tests end-to-end usando la salida del paso de build. Cualquier test end-to-end escrito previamente podría funcionar out-of-the-box, si has podido igualar el markup de tu sitio de Nuxt. Librerías de testing como Jest y Vue Testing Library pueden importarse y usarse en Astro para testear tus componentes de Vue.

Consulta la guía de testing de Astro para más.

Referencia: Convertir sintaxis de NuxtJS a Astro

Sección titulada “Referencia: Convertir sintaxis de NuxtJS a Astro”

Para usar variables locales en el HTML de un componente de Astro, cambia el conjunto de dos llaves por un único conjunto de llaves:

src/components/Component.astro
---
const message = "Hello!"
---
<p>{{message}}</p>
<p>{message}</p>

Para vincular un atributo o propiedad de componente en un componente de Astro, cambia esta sintaxis a lo siguiente:

src/components/Component.astro
---
---
<p v-bind:aria-label="message">...</p>
<!-- Or -->
<p :aria-label="message">...</p>
<!-- Also support component props -->
<Header title="Page"/>
<p aria-label={message}>...</p>
<!-- Also support component props -->
<Header title={"Page"}/>

Convierte cualquier componente <NuxtLink to=""> de Nuxt a etiquetas HTML <a href="">.

<NuxtLink to="/blog">Blog</Link>
<a href="/blog">Blog</a>

Astro no usa ningún componente especial para links, aunque eres libre de construir componentes de link personalizados. Luego puedes importar y usar este <Link> igual que cualquier otro componente.

src/components/Link.astro
---
const { to } = Astro.props
---
<a href={to}><slot /></a>

Si es necesario, actualiza cualquier importación de archivos para referenciar rutas de archivos relativas exactamente. Esto se puede hacer usando alias de importación, o escribiendo una ruta relativa completa.

Ten en cuenta que los archivos .astro y varios otros tipos de archivo deben importarse con su extensión completa.

src/pages/authors/Fred.astro
---
import Card from `../../components/Card.astro`;
---
<Card />

Generación de páginas dinámicas de Nuxt a Astro

Sección titulada “Generación de páginas dinámicas de Nuxt a Astro”

En Nuxt, para generar una página dinámica debes:

En Astro, de manera similar tienes dos opciones:

Convierte una función generate de Nuxt a una función getStaticPaths de Astro.

Sección titulada “Convertir una función generate de Nuxt a una función getStaticPaths de Astro.”

Para generar múltiples páginas, reemplaza la función para crear rutas en tu nuxt.config.js con getStaticPaths() directamente dentro de una página de enrutamiento dinámico:

nuxt.config.js
{
// ...
generate: {
async routes() {
// Axios is required here unless you're using Node 18
const res = await axios.get("https://pokeapi.co/api/v2/pokemon?limit=151")
const pokemons = res.data.results;
return pokemons.map(pokemon => {
return '/pokemon/' + pokemon.name
})
}
}
}
src/pages/pokemon/[name].astro
---
export const getStaticPaths = async () => {
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results;
return pokemons.map(({ name }) => ({
params: { name },
}))
}
// ...
---
<!-- Your template here -->

Nuxt tiene dos métodos para obtener datos del lado del servidor:

En Astro, obtén los datos dentro del code fence de tu página.

Migra lo siguiente:

pages/index.vue
{
// ...
async asyncData() {
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results;
return {
pokemons,
}
},
}

A un code fence sin una función wrapper:

src/pages/index.astro
---
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results;
---
<!-- Your template here -->

Nuxt utiliza el styling de componentes de Vue para generar el estilo de una página.

pages/index.vue
<template>
<!-- Your template here -->
</template>
<script>
// Your server logic here
</script>
<style scoped>
.class {
color: red;
}
</style>

De manera similar, en Astro puedes añadir un elemento <style> en la plantilla de tu página para proporcionar estilos con scope al componente.

src/pages/index.vue
---
// Your server logic here
---
<style>
.class {
color: red;
}
</style>

Las etiquetas <style> tienen scope por defecto en Astro. Para hacer que una etiqueta <style> sea global, márcala con el atributo is:global:

src/pages/index.vue
<style is:global>
p {
color: red;
}
</style>

Astro soporta los pre-procesadores CSS más populares instalándolos como una dependencia de desarrollo. Por ejemplo, para usar SCSS:

Ventana de la terminal
npm install -D sass

Después de hacer esto, podrás usar estilos .scss o .sass sin modificaciones desde tus componentes de Vue.

src/layouts/Layout.astro
<p>Hello, world</p>
<style lang="scss">
p {
color: black;
&:hover {
color: red;
}
}
</style>

Ve más sobre estilos en Astro.

Convierte cualquier componente Nuxt <nuxt-img/> o <nuxt-picture/> a el propio componente de imagen de Astro en archivos .astro o .mdx, o a una etiqueta HTML estándar <img> o <picture> según corresponda en tus componentes de Vue.

El componente <Image /> de Astro funciona solo en archivos .astro y .mdx. Consulta una lista completa de sus atributos de componente y ten en cuenta que varios diferirán de los atributos de Nuxt.

src/pages/index.astro
---
import { Image } from 'astro:assets';
import rocket from '../assets/rocket.png';
---
<Image src={rocket} alt="A rocketship in space." />
<img src={rocket.src} alt="A rocketship in space.">

En componentes de Vue (.vue) dentro de tu app de Astro, usa sintaxis de imagen JSX estándar (<img />). Astro no optimizará estas imágenes, pero puedes instalar y usar paquetes NPM para más flexibilidad.

Puedes aprender más sobre el uso de imágenes en Astro en la guía de Images.

Aquí hay un ejemplo de data fetching de la Pokédex de Nuxt convertido a Astro.

pages/index.vue obtiene y muestra una lista de los primeros 151 Pokémon usando la REST PokéAPI.

Aquí te mostramos cómo recrear eso en src/pages/index.astro, reemplazando asyncData() con fetch().

  1. Identifica el <template> y el <style> en el SFC de Vue.

    pages/index.vue
    <template>
    <ul class="plain-list pokeList">
    <li v-for="pokemon of pokemons" class="pokemonListItem" :key="pokemon.name">
    <NuxtLink class="pokemonContainer" :to="`/pokemon/${pokemon.name}`">
    <p class="pokemonId">No. {{pokemon.id}}</p>
    <img
    class="pokemonImage"
    :src="`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`"
    :alt="`${pokemon.name} picture`"/>
    <h2 class="pokemonName">{{pokemon.name}}</h2>
    </NuxtLink>
    </li>
    </ul>
    </template>
    <script>
    import Vue from 'vue'
    export default Vue.extend({
    name: 'IndexPage',
    layout: 'default',
    async asyncData() {
    const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
    const resJson = await res.json();
    const pokemons = resJson.results.map(pokemon => {
    const name = pokemon.name;
    // https://pokeapi.co/api/v2/pokemon/1/
    const url = pokemon.url;
    const id = url.split("/")[url.split("/").length - 2];
    return {
    name,
    url,
    id
    }
    });
    return {
    pokemons,
    }
    },
    head() {
    return {
    title: "Pokedex: Generation 1"
    }
    }
    });
    </script>
    <style scoped>
    .pokeList {
    display: grid;
    grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );
    gap: 1rem;
    }
    /* ... */
    </style>
  2. Crea src/pages/index.astro

    Usa las etiquetas <template> y <style> del SFC de Nuxt. Convierte cualquier sintaxis de Nuxt o Vue a Astro.

    Ten en cuenta que:

    • El <template> se elimina

    • El <style> tiene su atributo scoped eliminado

    • v-for se convierte en .map.

    • :attr="val" se convierte en attr={val}

    • <NuxtLink> se convierte en <a>.

    • El fragment <> </> no es necesario en las plantillas de Astro.

    src/pages/index.astro
    ---
    ---
    <ul class="plain-list pokeList">
    {pokemons.map((pokemon) => (
    <li class="pokemonListItem" key={pokemon.name}>
    <a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
    <p class="pokemonId">No. {pokemon.id}</p>
    <img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
    <h2 class="pokemonName">{pokemon.name}</h2>
    </a>
    </li>
    ))}
    </ul>
    <style>
    .pokeList {
    display: grid;
    grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );
    gap: 1rem;
    }
    /* ... */
    </style>
  3. Añade cualquier import, prop y JavaScript necesario

    Ten en cuenta que:

    • La función asyncData ya no es necesaria. Los datos de la API se obtienen directamente en el code fence.
    • A <Layout> component is imported, and wraps the page templating.
      • Nuestro método head() de Nuxt se pasa al componente <Layout>, que se pasa al elemento <title> como una propiedad.
    src/pages/index.astro
    ---
    import Layout from '../layouts/layout.astro';
    const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151");
    const resJson = await res.json();
    const pokemons = resJson.results.map(pokemon => {
    const name = pokemon.name;
    // https://pokeapi.co/api/v2/pokemon/1/
    const url = pokemon.url;
    const id = url.split("/")[url.split("/").length - 2];
    return {
    name,
    url,
    id
    }
    });
    ---
    <Layout title="Pokedex: Generation 1">
    <ul class="plain-list pokeList">
    {pokemons.map((pokemon) => (
    <li class="pokemonListItem" key={pokemon.name}>
    <a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
    <p class="pokemonId">No. {pokemon.id}</p>
    <img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
    <h2 class="pokemonName">{pokemon.name}</h2>
    </a>
    </li>
    ))}
    </ul>
    </Layout>
    <style>
    .pokeList {
    display: grid;
    grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );
    gap: 1rem;
    }
    /* ... */
    </style>

Más guías de migración

Contribuir Comunidad Patrocinar