Astro中的建立博客标签索引页
#javascript #教程 #typescript #astro

当我通过2023重新设计重建网站时。我也想添加博客标签收集页面。这样,人们就可以在我在博客文章中使用的标签上“过滤”。我想以易于维护的方式进行此操作,并且不需要我编写很多代码。因此,我对它进行了黑客攻击,并找到了一个非常整洁的解决方案。这是我如何实施它的一些指南。

目标

这个想法是根据我的博客文章的前材上放置的标签生成动态页面。因此,用户可以在标签上搜索或过滤并获取该标签的所有相关博客文章的列表。

Blogpost FrontMatter验证:

import { z, defineCollection } from "astro:content";

const blogCollection = defineCollection({
    schema: z.object({
        title: z.string(),
        author: z.string(),
        tags: z.array(z.string()),
        description: z.string(),
        pubDate: z.string().transform((str) => new Date(str)),
        imgUrl: z.string(),
        draft: z.boolean().optional().default(false),
    }),
});

您可以在上面的代码块中看到,标签是字符串数组。这意味着一个博客文章可以包含多个标签,一个标签可以具有多个博客文章。

构建动态页面

当然,标签是动态的,因此第一件事是使用Astro中的getStaticPaths()方法构建页面。就我而言,动态页面是/blog/tags/:tag。 Astro中的getStaticPaths()应该始终返回一个看起来像这样的对象:

return {
  params: { /* ... */ },
  props: { /* ... */ },
};

在我的情况下,这意味着params对象应返回标签字符串本身,并且Prop应返回具有与该标签相关的所有Blogposts的数组。

让我们从获取所有标签开始并构建静态页面:

---
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const allPosts = await getCollection('blog');

  const tags: string[] = [];

  // using .toLowerCase() here to get rid of case sensitivity
  allPosts.forEach((post) => {
    post.data.tags.forEach((tag) => {
      tags.push(tag.toLowerCase());
    });
  });

  // using a new array from a set, we can get rid of duplicate tags 
  return Array.from(new Set(tags)).map((tag) => {
    return {
      params: { tag },
      // only keep the blogposts that contain the tag itself
      props: {
        blogposts: allPosts.filter((post) => post.data.tags.map(tag => tag.toLowerCase()).includes(tag)),
      }
    };
  });
}
---

现在,当我们构建项目时,所有标签都会为其生成特定的页面。用一个称为blogposts的道具。我正在使用Astro v2.0 Content Collection API,但您也可以使用Astro.glob()

添加页面布局和标记

现在生成页面,我们想在用户输入该页面时显示一些内容。我们可以定义动态页面在前磨牙下的外观。

也不要忘记为页面本身添加您的Astro.props对象。

---
import { getCollection } from 'astro:content';
import type { CollectionEntry } from 'astro:content';

import Layout from '../../../layouts/YourLayout.astro';

export async function getStaticPaths() {
  const allPosts = await getCollection('blog');

  const tags: string[] = [];

  allPosts.forEach((post) => {
    post.data.tags.forEach((tag) => {
      tags.push(tag.toLowerCase());
    });
  });

  return Array.from(new Set(tags)).map((tag) => {
    return {
      params: { tag },
      props: {
        blogposts: allPosts.filter((post) => post.data.tags.map(tag => tag.toLowerCase()).includes(tag)),
      },
    };
  });
}

interface Props {
  tag: string;
  blogposts: CollectionEntry<'blog'>[];
}

const { blogposts } = Astro.props;
---
<Layout>
  <main>
    <ul>
      { blogposts.map(post => (
        <li>
          <a href={`/blog/${post.slug}`}>{post.data.title}</a>
        </li>
      ))}
    </ul>
  </main>
</Layout>

现在应该显示您的布局,并使用与标签相关的所有博客文章的列表!

请记住,您仍然应该自定义此页面以满足您的需求,否则有点丑陋。

如果您对我如何在my own website上实现此目标有兴趣,则可以随时窥视the source code