title | description | type | stub | framework | i18nReady |
---|---|---|---|---|---|
Migrando desde NuxtJS |
Consejos para migrar un proyecto existente de NuxtJS a Astro |
migration |
false |
NuxtJS |
true |
import AstroVueTabs from '/components/tabs/AstroVueTabs.astro'
import PackageManagerTabs from '/components/tabs/PackageManagerTabs.astro'
import { FileTree } from '@astrojs/starlight/components';
Aquí hay algunos conceptos clave y estragias de migración para ayudarte a comenzar. ¡Usa el resto de nuestra documentación y nuestra comunidad de Discord para continuar!
Esta guía se refiere a Nuxt 2, no a la nueva versión Nuxt 3. Si bien 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:
- Los proyectos de Astro también pueden ser SSG o SSR con prerenderizado a nivel de página.
- Astro usa un enrutamiento basado en archivos y permite que las páginas con nombres creen rutas dinámicas
- Astro es basado en componentes y la estructura de tu marcado será similar antes y después de tu migración.
- Astro tiene una integración oficial para usar componentes de Vue.
- Astro tiene soporte para instalar paquetes de NPM, incluyendo librerías de Vue. Puedes mantener algunos o todos tus componentes y dependencias de Vue existentes.
Cuando reconstruyas tu sitio Nuxt en Astro, notarás algunas diferencias importantes:
-
Nuxt es una SPA basada en Vue (aplicación de una sola página). Los sitios de Astro son aplicaciones multipágina construidas usando componentes
.astro
, pero también pueden admitir React, Preact, Vue.js, Svelte, SolidJS, AlpineJS, Lit y plantillas HTML sin procesar. -
Enrutamiento de Página: Nuxt usa
vue-router
para el enrutamiento SPA, yvue-meta
para administrar<head>
. En Astro, crearás rutas de página HTML separadas y controlarás tu<head>
directamente o en un componente plantilla. -
Orientado al contenido: Astro fue diseñado para mostrar tu contenido y permitirte optar por la interactividad solo cuando sea necesario. Una aplicación Nuxt existente puede estar construida para una alta interactividad del lado del cliente. Astro tiene capacidades incorporadas 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 los paneles de control.
Cada migración de proyecto se verá diferente, pero hay algunas acciones comunes que realizarás al convertir de Nuxt a Astro.
Utiliza el comando create astro
en tu gestor de paquetes para lanzar el asistente de linea de comandos de Astro o elige un tema de la comunidad del catálogo de temas de Astro.
Puedes pasar un argumento --template
al comando create astro
para iniciar un nuevo proyecto Astro con uno de nuestros ejemplos oficiales (por ejemplo, docs
, blog
, portfolio
). O, puedes iniciar un nuevo proyecto desde cualquier repositorio Astro existente en GitHub.
# crear un nuevo proyecto con un ejemplo oficial
npm create astro@latest -- --template <example-name>
```
</Fragment>
<Fragment slot="pnpm">
```shell
# lanzar el Asistente de línea de comandos de Astro
pnpm create astro@latest
# crear un nuevo proyecto con un ejemplo oficial
pnpm create astro@latest --template <example-name>
```
</Fragment>
<Fragment slot="yarn">
```shell
# lanzar el Asistente de línea de comandos de Astro
yarn create astro@latest
# crear un nuevo proyecto con un ejemplo oficial
yarn create astro@latest --template <example-name>
```
</Fragment>
Luego, copia tus archivos de proyecto Nuxt existentes a tu nuevo proyecto Astro en una carpeta separada fuera de src
.
:::tip Visita https://astro.new para ver la lista completa de plantillas de inicio oficiales y enlaces para abrir un nuevo proyecto en StackBlitz, CodeSandbox o Gitpod. :::
Puede resultarte útil instalar algunas de las integraciones opcionales de Astro para usar mientras estes migrando tu proyecto de Nuxt a Astro:
-
@astrojs/vue: para reutilizar algunos componentes de interfaz de usuario Vue existentes en tu nuevo sitio Astro, o seguir escribiendo con componentes Vue.
-
@astrojs/mdx: para traer archivos MDX existentes de tu proyecto Nuxt, o para usar MDX en tu nuevo sitio Astro.
-
Mueve el contenido de la carpeta
static/
de Nuxt apublic/
.Astro usa el directorio
public/
para los activos estáticos, similar a la carpetastatic/
de Nuxt. -
Copia o mueve los otros archivos y carpetas de Nuxt (por ejemplo,
pages
,layouts
, etc.) a la carpetasrc/
de Astro.Como Nuxt, la carpeta
src/pages/
de Astro es una carpeta especial utilizada para el enrutamiento basado en archivos. Todas las demás carpetas son opcionales, y puedes organizar el contenido de tu carpetasrc/
de la forma que desees. Otras carpetas comunes en los proyectos Astro incluyensrc/layouts/
,src/components
,src/styles
,src/scripts
.
Aqui hay algunos consejos para convertir un componente .vue
de Nuxt en un componente .astro
:
-
Usa la etiqueta
<template>
de la función de componente NuxtJS existente como base para tu plantilla HTML. -
Cambia cualquier sintaxis de Nuxt o Vue a Astro o a los estándares web de HTML. Esto incluye
<NuxtLink>
,:class
,{{variable}}
, yv-if
, por ejemplo. -
Mueve la etiqueta
<script>
de JavaScript, a un "valla de código" (---
). Convierte las propiedades de recuperación de datos de tu componente a JavaScript del lado del servidor - ver la obtención de datos de Nuxt en Astro. -
Utiliza
Astro.props
para acceder a cualquier prop adicional que se pasaba anteriormente a tu componente Vue. -
Decide si algún componente importado también necesita ser convertido a Astro. Con la integración oficial instalada, puedes usar componentes Vue existentes en tu archivo Astro. Pero, es posible que desees convertirlos a Astro, ¡especialmente si no necesitan ser interactivos!
Mira un ejemplo de una aplicación Nuxt convertida paso a paso.
Compara el siguiente componente de Nuxt con un componente de Astro:
```vue title="Page.vue"The repository you're looking up doesn't exist
Astro has {{stars}} 🧑🚀
<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>
```
const res = await fetch('https://api.github.com/repos/withastro/astro')
const json = await res.json()
const message = json.message;
const stars = json.stargazers_count || 0;
---
{message === "Not Found" ?
<p>The repository you're looking up doesn't exist</p> :
<>
<Header />
<p class="banner">Astro has {stars} 🧑🚀</p>
<Footer />
</>
}
<style>
.banner {
background-color: #f4f4f4;
padding: 1em 1.5em;
text-align: center;
margin-bottom: 1em;
}
<style>
```
Puede resultarte útil comenzar convirtiendo tus diseños y plantillas de Nuxt en componentes de diseño 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 estas.
Ten en cuenta la plantilla HTML estándar y el acceso directo a <head>
:
<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>
<!-- Envuelve el elemento slot con el diseño existente de plantilla -->
<slot />
</body>
</html>
También puede que desees reutilizar el 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 ni 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 páginas viven en /pages
. En Astro, todo el contenido de tu página debe vivir dentro de src/pages
o src/content
.
Tus páginas Vue existentes (.vue
) de Nuxt deberán convertirse 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 en función de 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 la página:
- pages/ - pokemon/ - _name.vue - index.vue - nuxt.config.jsPara convertir a Astro, cambia esta propiedad de ruta dinámica subrayada (por ejemplo, _name.vue
) para que esté envuelta en un par de corchetes (por ejemplo, [name].astro
):
Astro tiene soporte incorporado para Markdown y una integración opcional para archivos MDX. Puedes reutilizar cualquier página Markdown y MDX existente, pero puede que requieran algunos ajustes en tu frontmatter, como agregar la propiedad especial layout
del frontmatter de Astro.
Ya no necesitarás crear manualmente páginas para cada ruta generada por Markdown o usar un paquete externo como @nuxt/content
. Estos archivos se pueden colocar dentro de src/pages/
para aprovechar el enrutamiento automático basado en archivos.
Cuando forma parte de una colección de contenido, los archivos Markdown y MDX vivirán en carpetas dentro de src/content/
y generarás esas páginas dinámicamente.
Como Astro genera HTML sin procesar, es posible escribir tests end-to-end utilizando la salida del paso de compilación. Cualquier test end-to-end escrito anteriormente podría funcionar de inmediato, si has podido hacer coincidir el marcado de tu sitio Nuxt. Las bibliotecas de pruebas como Jest y Vue Testing Library se pueden importar y usar en Astro para probar tus componentes Vue.
Ver más en la guía de pruebas de Astro.
Para usar variables locales en un componente de Astro HTML, cambia el conjunto de dos llaves a un conjunto de llaves:
---
const message = "Hello!"
---
<p>{{message}}</p>
<p>{message}</p>
Para vincular un atributo o una propiedad de componente en un componente de Astro, cambia la sintaxis a la siguiente:
---
---
<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 cualquiere 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 los enlaces, aunque puedes crear componentes de enlace personalizados. Luego puedes importar y usar este <Link>
como lo harías con cualquier otro componente.
---
const { to } = Astro.props
---
<a href={to}><slot /></a>
Si es necesario, actualiza cualquier importación de archivo para hacer referencia exacta a las rutas de archivo relativas. Esto se puede hacer usando alias de importación o escribiendo una ruta relativa completa.
Ten en cuenta que .astro
y varios otros tipos de archivo deben importarse con su extensión de archivo completa.
---
import Card from `../../components/Card.astro`;
---
<Card />
En Nuxt, para generar una página dinámica, debes:
- Usar renderizado del lado del servidor (SSR).
- Usar la función
generate
ennuxt.config.js
para definir todas las rutas estáticas posibles.
En astro, similarmente tienes dos opciones:
- Usar renderizado del lado del servidor (SSR).
- Exporta una función
getStaticPaths()
en el frontmatter de una página de Astro para indicarle al framework qué rutas estáticas generar dinámicamente.
Para generar varias 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:
{
// ...
generate: {
async routes() {
// Axios es requerido aquí a menos que estés usando 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
})
}
}
}
---
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 },
}))
}
// ...
---
<!-- Tu plantilla aquí -->
Nuxt tiene dos métodos para obtener datos del lado del servidor:
En Astro, obtén datos dentro de la valla de código de tu página.
Migra lo siguiente:
{
// ...
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,
}
},
}
Para una valla de código sin una función de envoltura:
---
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results;
---
<!-- Tu plantilla va aqui -->
Nuxt utiliza el estilo de los componentes de Vue para generar el estilo de una página.
<template>
<!-- Tu plantilla va aqui -->
</template>
<script>
// La lógica del servidor va aquí
</script>
<style scoped>
.class {
color: red;
}
</style>
Similarmente, en Astro puedes poner en una etiqueta <style>
en la plantilla de tu página para proporcionar estilos con alcance al componente.
---
// La lógica del servidor aquí
---
<style>
.class {
color: red;
}
</style>
Las etiquetas <style>
son scoped
(delimitadas) de forma predeterminada en Astro. Para hacer que una etiqueta <style>
sea global, márcala con el atributo is:global
:
<style is:global>
p {
color: red;
}
</style>
Astro admite los preprocedaores de CSS más populares instalandolos como una dependencia de desarrollo. Por ejemplo, para usar SCSS:
npm install -D sass
Después de hacerlo, puedes usar estilos .scss
o .sass
sin modificación desde tus componentes Vue.
<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-img/>
or <nuxt-picture/>
de Nuxt al componente de imagen propio de Astro en archivos .astro
o .mdx
, o al tag estándar <img>
o <picture>
de HTML 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 serán diferentes de los atributos de Nuxt.
---
import { Image } from 'astro:assets';
import rocket from '../assets/rocket.png';
---
<Image src={rocket} alt="Un cohete en el espacio." />
<img src={rocket.src} alt="Un cohete en el espacio.">
En los componentes Vue (.vue
) dentro de tu aplicación Astro, usa la sintaxis de imagen JSX estándar (<img />
). Astro no optimizará estas imágenes, pero puedes instalar y usar paquetes NPM para tener más flexibilidad.
Puedes aprender más sobre el uso de imágenes en Astro en la guía de imágenes.
Aquí hay un ejemplo de la obtención de datos de un Pokédex hecho con Nuxt migrado a Astro.
pages/index.vue
obtiene y muestra una lista de los primeros 151 Pokémon usando la REST PokéAPI.
Aquí está cómo recrearlo en src/pages/index.astro
, reemplazando asyncData()
con fetch()
.
-
Identifica las etiquetas
<template>
y<style>
en el SFC de 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="`Foto de ${pokemon.name}`"/> <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>
-
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:
-
la etiqueta
<template>
es eliminada -
la etiqueta
<style>
tiene su atributoscoped
eliminado -
v-for
se convierte en.map
. -
:attr="val"
se convierte enattr={val}
-
la etiqueta
<NuxtLink>
se convierte en<a>
. -
El fragmento
<> </>
no es requerido en la plantilla de 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={`Foto de ${pokemon.name}`}/> <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>
-
-
Agrega cualquier importación, propiedad y JavaScript necesario
Ten en cuenta que:
- La función
asyncData
no es necesaria. Los datos de la API se obtienen directamente en la valla de código. - Un componente
<Layout>
es importado y envuelve la plantilla de la página.- Nuestro método
head()
de Nuxt se pasa al componente<Layout>
, que se pasa al elemento<title>
como propiedad.
- Nuestro método
--- 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={`Foto de ${pokemon.name}`}/> <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>
- La función
- Artículo de Blog: De Nuxt a Astro - reconstruyendo con Astro
- Artículo de Blog: Cambio de plataforma de Nuxt 2 a Astro 3 - de configuración a producción