Пропустить до содержимого

Учебное пособие - Расширение с помощью коллекций контента

Коллекции контента - это мощный способ управления группами схожего контента, например, записями в блоге. Коллекции помогают упорядочить документы, проверить YAML frontmatter и обеспечить автоматическую безопасность типов TypeScript для всего контента (даже если вы сами не пишете на TypeScript).

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

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

Вам понадобится существующий проект Astro с файлами Markdown или MDX в папке src/pages/.

В этом уроке используется готовый проект Готовый проект учебника “Создание блога” для демонстрации конвертации блога в коллекции контента. Вы можете использовать эту кодовую базу локально или завершить руководство в браузере, отредактировав код руководства по блогу в StackBlitz.

Вместо этого вы можете выполнить эти шаги в своем собственном проекте Astro, но вам нужно будет скорректировать инструкции для вашей кодовой базы.

Мы рекомендуем использовать наш пример проекта для завершения этого краткого руководства. Затем вы сможете использовать полученные знания для создания коллекций контента в своем собственном проекте.

Из вводного руководства по созданию блога вы узнали о встроенной файловой маршрутизации Astro: любой файл .astro, .md или .mdx в папке src/pages/ автоматически становился страницей на вашем сайте.

Чтобы создать свой первый пост в блоге по адресу https://example.com/posts/post-1/, вы создали папку /posts/ и добавили в нее файл post-1.md. Затем вы добавляли новый файл в формате Markdown в эту папку каждый раз, когда хотели добавить новую запись в блог на свой сайт.

Даже при использовании коллекций контента вы все равно будете использовать папку src/pages/ для отдельных страниц, например, для страницы “Обо мне”. Но перемещение записей блога в специальную папку src/content/ позволит вам использовать более мощные и эффективные API для создания индекса записей блога и отображения отдельных записей блога.

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

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

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

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

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

Расширение учебного пособия по созданию блога с помощью коллекций контента

Заголовок раздела Расширение учебного пособия по созданию блога с помощью коллекций контента

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

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

    Terminal window
    # Обновите до Astro v4.x
    npm install astro@latest
    # Пример: обновите учебное пособие по блогам Интеграция с Preact
    npm install @astrojs/preact@latest
  2. В этом руководстве для блога используется base (наименее строгая) настройка TypeScript. Чтобы использовать коллекции контента, необходимо настроить TypeScript для коллекций контента либо с помощью настройки strict или strictest, либо путем добавления двух опций в tsconfig.json.

    Чтобы использовать коллекции контента без написания TypeScript в остальной части примера, приведенного в учебнике, добавьте следующие две опции TypeScript в файл конфигурации:

    tsconfig.json
    {
    // Примечание: Не нужно ничего менять, если вы используете "astro/tsconfigs/strict" или "astro/tsconfigs/strictest".
    "extends": "astro/tsconfigs/base",
    "compilerOptions": {
    "strictNullChecks": true,
    "allowJs": true
    }
    }

Создайте коллекцию для записей в блоге

Заголовок раздела Создайте коллекцию для записей в блоге
  1. Создайте новую коллекцию (папку) под названием src/content/posts/.

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

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

    src/content/config.ts
    // Импортируйте утилиты из `astro:content`.
    import { z, defineCollection } from "astro:content";
    // Определите `type` и `schema` для каждой коллекции
    const postsCollection = defineCollection({
    type: 'content',
    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 = {
    posts: postsCollection,
    };
  4. Чтобы Astro распознала вашу схему, выйдите из dev-сервера (CTRL + C) и выполните следующую команду: npx astro sync. Это определит модуль astro:content для API коллекций содержимого. Перезапустите сервер dev, чтобы продолжить обучение.

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

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

    src/pages/posts/[...slug].astro
    ---
    import { getCollection } from 'astro:content';
    import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';
    export async function getStaticPaths() {
    const blogEntries = await getCollection('posts');
    return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
    }));
    }
    const { entry } = Astro.props;
    const { Content } = await entry.render();
    ---
  3. Оформите ваш пост <Content /> в макете для страниц Markdown. Это позволяет задать общий макет для всех ваших постов.

    src/pages/posts/[...slug].astro
    ---
    import { getCollection } from 'astro:content';
    import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';
    export async function getStaticPaths() {
    const blogEntries = await getCollection('posts');
    return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
    }));
    }
    const { entry } = Astro.props;
    const { Content } = await entry.render();
    ---
    <MarkdownPostLayout frontmatter={entry.data}>
    <Content />
    </MarkdownPostLayout>
  4. Удалите определение layout в frontmatter каждого отдельного поста. Теперь ваше содержимое будет обернуто в макет при рендеринге, и это свойство больше не нужно.

    src/content/posts/post-1.md
    ---
    layout: ../../layouts/MarkdownPostLayout.astro
    title: 'My First Blog Post'
    pubDate: 2022-07-01
    ...
    ---
  1. В любом месте, где у вас есть список записей блога, например на странице “Блог” учебника (src/pages/blog.astro/), вам нужно заменить Astro.glob() на getCollection() в качестве способа получения контента и метаданных из ваших файлов 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 = await Astro.glob("../pages/posts/*.md");
    const allPosts = await getCollection("posts");
    ---
  2. Вам также нужно будет обновить ссылки на данные, возвращаемые для каждого post. Теперь вы найдете значения frontmatter в свойстве 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 = "My Astro Learning Blog";
    const allPosts = await getCollection("posts");
    ---
    <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.slug}/`} title={post.data.title} />
    ))
    }
    </ul>
    </BaseLayout>
  3. Проект обучающего блога также динамически генерирует страницу для каждого тега с помощью src/pages/tags/[tag].astro и отображает список тегов в src/pages/tags/index.astro.

    Внесите в эти два файла те же изменения, что и выше:

    • получите данные обо всех записях вашего блога, используя getCollection("posts") вместо Astro.glob()
    • получите доступ ко всем значениям frontmatter, используя 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("posts");
    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.slug}/`} 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("posts");
    const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
    const pageTitle = "Tag Index";
    ---
    ...

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

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

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

    Чтобы вывести дату в макете записи блога, преобразуйте ее в строку:

    src/layouts/MarkdownPostLayout.astro
    ...
    <BaseLayout pageTitle={frontmatter.title}>
    <p>{frontmatter.pubDate.toString().slice(0,10)}</p>
    <p><em>{frontmatter.description}</em></p>
    <p>Written by: {frontmatter.author}</p>
    <img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} />
    ...
  1. Наконец, проект обучающего блога включает в себя 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("posts");
    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.slug}/`,
    })),
    customData: `<language>en-us</language>`,
    })
    }

Полный пример учебника по блогам с использованием коллекций контента смотрите в ветке Коллекции контента репозитория учебника.