Saltar al contenido

Opcional: Crea una colección de contenido

Ahora que tienes un blog usando el enrutamiento basado en archivos integrado de Astro, lo actualizarás para usar una colección de contenido. Las colecciones de contenido son una forma poderosa de gestionar grupos de contenido similar, como posts de blog.

Prepárate para…

  • Mueve tu carpeta de posts de blog a src/blog/
  • Crea un schema para definir el frontmatter de tus posts de blog
  • Usa getCollection() para obtener contenido y metadatos de los posts de blog

Incluso al usar colecciones de contenido, seguirás usando la carpeta src/pages/ para páginas individuales, como tu página About Me. Pero, mover tus posts de blog fuera de esta carpeta especial te permitirá usar APIs más potentes y eficientes para generar tu índice de posts de blog y mostrar tus posts de blog individuales.

Al mismo tiempo, recibirás mejor guía y autocompletado en tu editor de código porque tendrás un schema para definir una estructura común para cada post que Astro te ayudará a aplicar a través de Zod, una librería de declaración y validación de schemas para TypeScript. En tu schema, puedes especificar cuándo las propiedades de frontmatter son obligatorias, como una descripción o un autor, y qué tipo de dato debe ser cada propiedad, como un string o un array. Esto lleva a detectar muchos errores antes, con mensajes de error descriptivos que te dicen exactamente cuál es el problema.

Lee más sobre las colecciones de contenido de Astro en nuestra guía, o comienza con las instrucciones a continuación para convertir un blog básico de src/pages/posts/ a src/blog/.

  1. ¿Qué tipo de página probablemente mantendrías en src/pages/?

  2. ¿Cuál no es un beneficio de mover los posts de blog a una colección de contenido?

  3. Las colecciones de contenido usan TypeScript . . .

Los pasos a continuación te muestran cómo extender el producto final del tutorial Construye un Blog creando una colección de contenido para los posts de blog.

Actualiza a la última versión de Astro, y actualiza todas las integraciones a sus últimas versiones ejecutando los siguientes comandos en tu terminal:

Ventana de la terminal
# Upgrade Astro and official integrations together
npx @astrojs/upgrade
  1. Crea una nueva colección (carpeta) llamada src/blog/.

  2. Mueve todos tus posts de blog existentes (archivos .md) de src/pages/posts/ a esta nueva colección.

  3. Crea un archivo src/content.config.ts para definir un schema para tu postsCollection. Para el código existente del tutorial de blog, añade el siguiente contenido al archivo para definir todas las propiedades de frontmatter usadas en sus posts de blog:

    src/content.config.ts
    // Import the glob loader
    import { glob } from "astro/loaders";
    // Import utilities from `astro:content`
    import { defineCollection } from "astro:content";
    // Import Zod
    import { z } from "astro/zod";
    // Define a `loader` and `schema` for each collection
    const blog = defineCollection({
    loader: glob({ pattern: '**/[^_]*.md', base: "./src/blog" }),
    schema: z.object({
    title: z.string(),
    pubDate: z.date(),
    description: z.string(),
    author: z.string(),
    image: z.object({
    url: z.string(),
    alt: z.string()
    }),
    tags: z.array(z.string())
    })
    });
    // Export a single `collections` object to register your collection(s)
    export const collections = { blog };
  4. Para que Astro reconozca tu schema, detén (CTRL + C) y reinicia el dev server para continuar con el tutorial. Esto definirá el módulo astro:content.

  1. Crea un archivo de página llamado src/pages/posts/[...slug].astro. Tus archivos Markdown y MDX ya no se convierten automáticamente en páginas usando el enrutamiento basado en archivos de Astro cuando están dentro de una colección, así que debes crear una página responsable de generar cada post de blog individual.

  2. Añade el siguiente código para consultar tu colección y hacer que el slug y el contenido de la página de cada post de blog estén disponibles para cada página que generará:

    src/pages/posts/[...slug].astro
    ---
    import { getCollection, render } from 'astro:content';
    export async function getStaticPaths() {
    const posts = await getCollection('blog');
    return posts.map(post => ({
    params: { slug: post.id }, props: { post },
    }));
    }
    const { post } = Astro.props;
    const { Content } = await render(post);
    ---
  3. Renderiza tu <Content /> del post dentro del layout para páginas Markdown. Esto te permite especificar un layout común para todos tus posts.

    src/pages/posts/[...slug].astro
    ---
    import { getCollection, render } from 'astro:content';
    import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';
    export async function getStaticPaths() {
    const posts = await getCollection('blog');
    return posts.map(post => ({
    params: { slug: post.id }, props: { post },
    }));
    }
    const { post } = Astro.props;
    const { Content } = await render(post);
    ---
    <MarkdownPostLayout frontmatter={post.data}>
    <Content />
    </MarkdownPostLayout>
  4. Elimina la definición de layout en el frontmatter de cada post individual. Tu contenido ahora está envuelto en un layout al renderizarse, y esta propiedad ya no es necesaria.

    src/content/posts/post-1.md
    ---
    layout: ../../layouts/MarkdownPostLayout.astro
    title: 'My First Blog Post'
    pubDate: 2022-07-01
    ...
    ---

Reemplaza import.meta.glob() con getCollection()

Sección titulada “Reemplaza import.meta.glob() con getCollection()”
  1. Dondequiera que tengas una lista de posts de blog, como la página Blog del tutorial (src/pages/blog.astro), necesitarás reemplazar import.meta.glob() con getCollection() como la forma de obtener contenido y metadatos de tus archivos Markdown.

    src/pages/blog.astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../layouts/BaseLayout.astro";
    import BlogPost from "../components/BlogPost.astro";
    const pageTitle = "My Astro Learning Blog";
    const allPosts = Object.values(import.meta.glob("../pages/posts/*.md", { eager: true }));
    const allPosts = await getCollection("blog");
    ---
  2. También necesitarás actualizar las referencias a los datos retornados para cada post. Ahora encontrarás tus valores de frontmatter en la propiedad data de cada objeto. Además, al usar colecciones cada objeto post tendrá un slug de página, no una URL completa.

    src/pages/blog.astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../layouts/BaseLayout.astro";
    import BlogPost from "../components/BlogPost.astro";
    const pageTitle = "My Astro Learning Blog";
    const allPosts = await getCollection("blog");
    ---
    <BaseLayout pageTitle={pageTitle}>
    <p>This is where I will post about my journey learning Astro.</p>
    <ul>
    {
    allPosts.map((post) => (
    <BlogPost url={post.url} title={post.frontmatter.title} />)}
    <BlogPost url={`/posts/${post.id}/`} title={post.data.title} />
    ))
    }
    </ul>
    </BaseLayout>
  3. El proyecto de blog del tutorial también genera dinámicamente una página para cada etiqueta usando src/pages/tags/[tag].astro y muestra una lista de etiquetas en src/pages/tags/index.astro.

    Aplica los mismos cambios que arriba a estos dos archivos:

    • obtiene datos sobre todos tus posts de blog usando getCollection("blog") en lugar de usar import.meta.glob()
    • accede a todos los valores de frontmatter usando data en lugar de frontmatter
    • crea una URL de página añadiendo el slug del post a la ruta /posts/

    La página que genera páginas de etiquetas individuales ahora se convierte en:

    src/pages/tags/[tag].astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../../layouts/BaseLayout.astro";
    import BlogPost from "../../components/BlogPost.astro";
    export async function getStaticPaths() {
    const allPosts = await getCollection("blog");
    const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
    return uniqueTags.map((tag) => {
    const filteredPosts = allPosts.filter((post) =>
    post.data.tags.includes(tag)
    );
    return {
    params: { tag },
    props: { posts: filteredPosts },
    };
    });
    }
    const { tag } = Astro.params;
    const { posts } = Astro.props;
    ---
    <BaseLayout pageTitle={tag}>
    <p>Posts tagged with {tag}</p>
    <ul>
    { posts.map((post) => <BlogPost url={`/posts/${post.id}/`} title={post.data.title} />) }
    </ul>
    </BaseLayout>

    Pruébalo tú mismo - Actualiza la consulta en la página de Índice de Etiquetas

    Sección titulada “Pruébalo tú mismo - Actualiza la consulta en la página de Índice de Etiquetas”

    Importa y usa getCollection para obtener las etiquetas usadas en los posts de blog en src/pages/tags/index.astro, siguiendo los mismos pasos de arriba.

    Muéstrame el código.
    src/pages/tags/index.astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../../layouts/BaseLayout.astro";
    const allPosts = await getCollection("blog");
    const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
    const pageTitle = "Tag Index";
    ---
    <!-- ... -->

Actualiza cualquier valor de frontmatter para coincidir con tu schema

Sección titulada “Actualiza cualquier valor de frontmatter para coincidir con tu schema”

Si es necesario, actualiza cualquier valor de frontmatter en todo tu proyecto, como en tu layout, que no coincida con el schema de tus colecciones.

En el ejemplo del tutorial de blog, pubDate era un string. Ahora, según el schema que define los tipos para el frontmatter del post, pubDate será un objeto Date. Ahora puedes aprovechar esto para usar los métodos disponibles para cualquier objeto Date para formatear la fecha.

Para renderizar la fecha en el layout del post de blog, conviértela a un string usando el método toLocaleDateString():

src/layouts/MarkdownPostLayout.astro
<!-- ... -->
<BaseLayout pageTitle={frontmatter.title}>
<p>{frontmatter.pubDate.toLocaleDateString()}</p>
<p><em>{frontmatter.description}</em></p>
<p>Written by: {frontmatter.author}</p>
<img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} />
<!-- ... -->

El proyecto de blog del tutorial incluye un feed RSS. Esta función también debe usar getCollection() para retornar información de tus posts de blog. Luego generarás los items RSS usando el objeto data retornado.

src/pages/rss.xml.js
import rss from '@astrojs/rss';
import { pagesGlobToRssItems } from '@astrojs/rss';
import { getCollection } from 'astro:content';
export async function GET(context) {
const posts = await getCollection("blog");
return rss({
title: 'Astro Learner | Blog',
description: 'My journey learning Astro',
site: context.site,
items: await pagesGlobToRssItems(import.meta.glob('./**/*.md')),
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.pubDate,
description: post.data.description,
link: `/posts/${post.id}/`,
})),
customData: `<language>en-us</language>`,
})
}

Para el ejemplo completo del tutorial de blog usando colecciones de contenido, consulta la rama Content Collections del repo del tutorial.

Contribuir Comunidad Patrocinar