По желанию: Создайте коллекцию контента
Теперь, когда у вас есть блог, использующий встроенную маршрутизацию на основе файлов в 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 в репозитории учебного проекта.