По желанию: Создайте коллекцию контента
Теперь, когда у вас есть блог, использующий встроенную маршрутизацию на основе файлов в Astro, вы обновите его, чтобы использовать коллекции контента. Коллекции контента — это мощный способ управления группами похожего контента, например, постами в блоге.
Приготовьтесь…
- Переместить папку с постами блога в
src/blog/
- Создать схему для определения метаданных ваших постов
- Использовать
getCollection()
для получения контента и метаданных постов
Изучите: страницы в сравнении с коллекциями
Заголовок раздела Изучите: страницы в сравнении с коллекциямиДаже при использовании коллекций контента, вы по-прежнему будете использовать папку src/pages/
для отдельных страниц, таких как страница «О сайте». Однако перемещение ваших постов блога за пределы этой специальной папки позволит вам использовать более мощные и производительные API для создания индекса постов и отображения отдельных записей блога.
В то же время, вы получите лучшие подсказки и автодополнение в вашем редакторе кода, так как у вас будет схема, определяющая общую структуру для каждого поста. Astro поможет вам соблюдать эту структуру с помощью Zod, библиотеки для объявления и валидации схем в TypeScript. В вашей схеме вы можете указать, какие свойства метаданных являются обязательными, например, описание или автор, и какой тип данных должен быть у каждого свойства, например, строка или массив. Это позволяет быстрее обнаруживать ошибки, с описательными сообщениями, которые точно указывают на проблему.
Узнайте больше о коллекциях контента в Astro в нашем руководстве или начните с инструкций ниже, чтобы преобразовать базовый блог из src/pages/posts/
в src/blog/
.
Проверьте свои знания
Заголовок раздела Проверьте свои знания-
Какой тип страницы вы, вероятно, оставите в
src/pages/
? -
Что не является преимуществом перемещения постов блога в коллекцию контента?
-
Коллекции контента используют TypeScript…
Шаги ниже показывают, как расширить финальный продукт руководства «Создание блога», создав коллекцию контента для постов блога.
Обновите зависимости
Заголовок раздела Обновите зависимостиОбновите до последней версии Astro и все используемые интеграции, выполнив следующие команды в терминале:
# Обновляем Astro вместе с официальными интеграциямиnpx @astrojs/upgrade
# Обновляем Astro вместе с официальными интеграциямиpnpm dlx @astrojs/upgrade
# Обновляем Astro вместе с официальными интеграциямиyarn dlx @astrojs/upgrade
Создайте коллекцию для ваших постов
Заголовок раздела Создайте коллекцию для ваших постов-
Создайте новую коллекцию (папку) с именем
src/blog/
. -
Переместите все ваши существующие посты блога (файлы
.md
) изsrc/pages/posts/
в эту новую коллекцию. -
Создайте файл
src/content.config.ts
, чтобы определить схему для вашейpostsCollection
. Для существующего кода руководства по созданию блога добавьте следующее содержимое в файл, чтобы определить все свойства метаданных, используемые в его постах:src/content.config.ts // Импортируем загрузчик globimport { 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 }; -
Чтобы Astro распознал вашу схему, завершите работу (
CTRL + C
) и перезапустите сервер разработки, чтобы продолжить обучение. Это определит модульastro:content
.
Сгенерируйте страницы из коллекции
Заголовок раздела Сгенерируйте страницы из коллекции-
Создайте файл страницы с именем
src/pages/posts/[...slug].astro
. Ваши файлы Markdown и MDX больше не становятся автоматически страницами при использовании маршрутизации на основе файлов в Astro, когда они находятся внутри коллекции, поэтому вы должны создать страницу, отвечающую за генерацию каждого отдельного поста блога. -
Добавьте следующий код, чтобы запросить вашу коллекцию и сделать 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);--- -
Отобразите ваш пост
<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> -
Удалите определение
layout
в метаданных каждого отдельного поста. Ваш контент теперь обёрнут в макет при отображении, и это свойство больше не требуется.src/content/posts/post-1.md ---layout: ../../layouts/MarkdownPostLayout.astrotitle: 'Моя первая запись в блоге'pubDate: 2022-07-01...---
Замените import.meta.glob()
на getCollection()
Заголовок раздела Замените import.meta.glob() на getCollection()-
Везде, где у вас есть список постов блога, например, на странице блога в руководстве (
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");--- -
Вам также потребуется обновить ссылки на данные, возвращаемые для каждого
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> -
В проекте учебного блога также динамически генерируется страница для каждого тега с использованием
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()
:
<!-- ... --><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
Заголовок раздела Обновите функцию RSSПроект учебного блога включает RSS-ленту. Эта функция также должна использовать getCollection()
, чтобы возвращать информацию из ваших постов. Затем вы сгенерируете элементы RSS с помощью возвращаемого объекта data
.
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 в репозитории учебного проекта.