Перейти к содержимому

По желанию: Создайте коллекцию контента

Теперь, когда у вас есть блог, использующий встроенную маршрутизацию на основе файлов в Astro, вы обновите его, чтобы использовать коллекции контента. Коллекции контента — это мощный способ управления группами похожего контента, например, постами в блоге.

Приготовьтесь…

  • Переместить папку с постами блога в src/blog/
  • Создать схему для определения метаданных ваших постов
  • Использовать getCollection() для получения контента и метаданных постов

Изучите: страницы в сравнении с коллекциями

Заголовок раздела Изучите: страницы в сравнении с коллекциями

Даже при использовании коллекций контента, вы по-прежнему будете использовать папку src/pages/ для отдельных страниц, таких как страница «О сайте». Однако перемещение ваших постов блога за пределы этой специальной папки позволит вам использовать более мощные и производительные API для создания индекса постов и отображения отдельных записей блога.

В то же время, вы получите лучшие подсказки и автодополнение в вашем редакторе кода, так как у вас будет схема, определяющая общую структуру для каждого поста. Astro поможет вам соблюдать эту структуру с помощью Zod, библиотеки для объявления и валидации схем в TypeScript. В вашей схеме вы можете указать, какие свойства метаданных являются обязательными, например, описание или автор, и какой тип данных должен быть у каждого свойства, например, строка или массив. Это позволяет быстрее обнаруживать ошибки, с описательными сообщениями, которые точно указывают на проблему.

Узнайте больше о коллекциях контента в Astro в нашем руководстве или начните с инструкций ниже, чтобы преобразовать базовый блог из src/pages/posts/ в src/blog/.

  1. Какой тип страницы вы, вероятно, оставите в src/pages/?

  2. Что не является преимуществом перемещения постов блога в коллекцию контента?

  3. Коллекции контента используют TypeScript…

Шаги ниже показывают, как расширить финальный продукт руководства «Создание блога», создав коллекцию контента для постов блога.

Обновите до последней версии Astro и все используемые интеграции, выполнив следующие команды в терминале:

Окно терминала
# Обновляем Astro вместе с официальными интеграциями
npx @astrojs/upgrade
  1. Создайте новую коллекцию (папку) с именем src/blog/.

  2. Переместите все ваши существующие посты блога (файлы .md) из src/pages/posts/ в эту новую коллекцию.

  3. Создайте файл src/content.config.ts, чтобы определить схему для вашей postsCollection. Для существующего кода руководства по созданию блога добавьте следующее содержимое в файл, чтобы определить все свойства метаданных, используемые в его постах:

    src/content.config.ts
    // Импортируем загрузчик glob
    import { glob } from "astro/loaders";
    // Импортируем утилиты из `astro:content`
    import { z, defineCollection } from "astro:content";
    // Определяем `loader` и `schema` для каждой коллекции
    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())
    })
    });
    // Экспортируем объект `collections` для регистрации ваших коллекций
    export const collections = { blog };
  4. Чтобы Astro распознал вашу схему, завершите работу (CTRL + C) и перезапустите сервер разработки, чтобы продолжить обучение. Это определит модуль astro:content.

  1. Создайте файл страницы с именем src/pages/posts/[...slug].astro. Ваши файлы Markdown и MDX больше не становятся автоматически страницами при использовании маршрутизации на основе файлов в Astro, когда они находятся внутри коллекции, поэтому вы должны создать страницу, отвечающую за генерацию каждого отдельного поста блога.

  2. Добавьте следующий код, чтобы запросить вашу коллекцию и сделать slug и содержимое всех страниц доступными для генерации соответствующих постов.

    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. Отобразите ваш пост <Content /> внутри макета для страниц Markdown. Это позволяет вам задать общий макет для всех ваших постов.

    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. Удалите определение layout в метаданных каждого отдельного поста. Ваш контент теперь обёрнут в макет при отображении, и это свойство больше не требуется.

    src/content/posts/post-1.md
    ---
    layout: ../../layouts/MarkdownPostLayout.astro
    title: 'Моя первая запись в блоге'
    pubDate: 2022-07-01
    ...
    ---
  1. Везде, где у вас есть список постов блога, например, на странице блога в руководстве (src/pages/blog.astro/), вам нужно заменить import.meta.glob() на getCollection() (EN) для получения контента и метаданных из ваших файлов Markdown.

    src/pages/blog.astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../layouts/BaseLayout.astro";
    import BlogPost from "../components/BlogPost.astro";
    const pageTitle = "Мой блог об изучении Astro";
    const allPosts = Object.values(import.meta.glob("../pages/posts/*.md", { eager: true }));
    const allPosts = await getCollection("blog");
    ---
  2. Вам также потребуется обновить ссылки на данные, возвращаемые для каждого post. Теперь значения метаданных будут находиться в свойстве data каждого объекта. Кроме того, при использовании коллекций каждый объект post будет иметь slug страницы, а не полный URL.

    src/pages/blog.astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../layouts/BaseLayout.astro";
    import BlogPost from "../components/BlogPost.astro";
    const pageTitle = "Мой блог об изучении Astro";
    const allPosts = await getCollection("blog");
    ---
    <BaseLayout pageTitle={pageTitle}>
    <p>Здесь я буду публиковать записи о своем пути изучения 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. В проекте учебного блога также динамически генерируется страница для каждого тега с использованием src/pages/tags/[tag].astro, а список тегов отображается на src/pages/tags/index.astro.

    Примените те же изменения, что и выше, к этим двум файлам:

    • получайте данные о всех постах блога с помощью getCollection("blog") вместо import.meta.glob()
    • обращайтесь к значениям метаданных через data, а не frontmatter
    • создавайте URL страницы, добавляя slug поста к пути /posts/

    Страница, которая генерирует отдельные страницы тегов, теперь будет выглядеть так:

    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>Посты с тегом {tag}</p>
    <ul>
    { posts.map((post) => <BlogPost url={`/posts/${post.id}/`} title={post.data.title} />) }
    </ul>
    </BaseLayout>

    Попробуйте сами - Обновите запрос на странице индекса тегов

    Заголовок раздела Попробуйте сами - Обновите запрос на странице индекса тегов

    Импортируйте и используйте getCollection, чтобы получить теги, используемые в постах блога на src/pages/tags/index.astro, следуя тем же шагам, что и выше.

    Покажите мне код!
    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 = "Индекс тегов";
    ---
    <!-- ... -->

Обновите значения метаданных, чтобы они соответствовали вашей схеме

Заголовок раздела Обновите значения метаданных, чтобы они соответствовали вашей схеме

При необходимости обновите значения метаданных во всем проекте, например, в макете, которые не соответствуют вашей схеме коллекций.

В примере учебного блога свойство pubDate было строкой. Теперь, согласно схеме, которая определяет типы для метаданных поста, pubDate будет объектом Date. Теперь вы можете воспользоваться этим, чтобы использовать методы, доступные для любого объекта Date, для форматирования даты.

Чтобы отобразить дату в макете поста блога, преобразуйте её в строку с помощью метода toLocaleDateString():

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

Проект учебного блога включает RSS-ленту. Эта функция также должна использовать getCollection(), чтобы возвращать информацию из ваших постов. Затем вы сгенерируете элементы RSS с помощью возвращаемого объекта data.

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 | Блог',
description: 'Мое путешествие по изучению 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>ru-ru</language>`,
})
}

Для полного примера руководства по блогу с использованием коллекций контента, ознакомьтесь с веткой Content Collections в репозитории учебного проекта.

Внести свой вклад Сообщество Sponsor