在将我的Jekyll博客转换为Astro时,最棘手的部分之一是确保所有 URL 保持不变。我的博客已经存在将近 20 年了,它已经从不同的框架多次转换,最终导致了一些不同的 URL 格式。然而,我最近使用的是熟悉的/blog/year/month/title
格式。在这篇文章中,我将描述如何在 Astro 中创建这种 URL 格式。
第一步:创建帖子页面
假设您的博客模板位于src/pages/blog
中,创建src/pages/blog/[...slug].astro
。 ...slug
周围的方括号是故意的,让 Astro 知道这个页面有动态路线。在这种情况下,您将用您帖子的 URL 路径替换..slug
。 ...
表示slug
的值可能包含斜杠。当您定义getStaticPaths()
方法时,Astro 知道如何替换slug
。
第 2 步:导出getStaticPaths()
Astro 调用getStaticPaths()
函数来确定如何呈现动态路线。该函数必须返回一个对象数组,每个对象至少包含一个params
键,指示如何填写文件名的动态部分,以及可选的props
键,该键包含页面呈现时在Astro.props
中可用的数据。
要为每篇博文创建合适的 URL,您需要为您收藏中的每篇博文导出一个slug
。这是执行此操作的代码:
import { getCollection } from 'astro:content' ; export async function getStaticPaths () { const posts = await getCollection ( 'blog' ); return posts . map ( post => { const date = post.data.pubDate; const year = post. getFullYear (); const month = (post. getMonth () + 1 ). padStart ( 2 , "0" ); return { params: { slug: `${ year }/${ month }/${ post . slug }` }, props: { post } } }); };
此代码的第一步是获取blog
集合中的所有博客文章。然后检查每个帖子以构建正确的 URL 格式。
如果您使用推荐的 Astro 设置,您将拥有一个包含帖子发布日期的post.data.pubDate
属性(属性的名称是可配置的——唯一重要的是它是一个日期对象。)对于方便起见,年和月被分配给变量。年份使用getFullYear()
保证四位数;月份是从 0 开始的,因此添加一个以获得日历年,然后调用padStart(2 "0")
以确保始终至少有两位数(即7
变为07
)。
对于每个帖子,代码返回一个包含params
键的对象,该键包含带有 URL 路径的slug
属性(重要的是 URL 路径不以斜杠结尾,因为这将导致页面无法呈现)和一个props
包含帖子本身的键。
第 3 步:访问要渲染的params
和props
在同一个src/pages/blog/[...slug].astro
文件中,在getStaticPaths()
之后,您可以从渲染每个页面的代码开始。首先,您需要收集有关从Astro.params
和Astro.props
呈现的页面的信息,如下所示:
const post = Astro.props; const { Content } = await post. render ();
现在你有了年份和那一年的帖子,所以你需要做的就是渲染帖子。这是一个例子:
< main > < h1 > { post.data.title } </ h1 > < Content /> </ main >
奖励步骤:重构以便于重用
虽然上面的代码作为示例效果很好,但实际上您不希望仅在src/pages/blog/[...slug].astro
中构造您的 URL,因为您还想在其他地方引用这些 URL(即,在您的博客索引页面上)。与其在各处复制该逻辑,不如创建一个将为您格式化 URL 的辅助函数,例如:
import { getCollection } from 'astro:content' ; export async function loadAndFormatCollection ( name ) { const posts = await getCollection (name); posts. forEach ( post => { const date = post.data.pubDate; const year = post. getFullYear (); const month = (post. getMonth () + 1 ). padStart ( 2 , "0" ); post.slug = `${ year }/${ month }/${ post . slug }` ; }); return posts; };
然后在src/pages/blog/[...slug].astro
你可以将代码简化为:
import { loadAndFormatCollection } from '../../lib/util.js' ; export async function getStaticPaths () { const posts = await loadAndFormatCollection ( 'blog' ); return posts . map ( post => { return { params: { slug: post.slug }, props: { post } } }); };
任何时候你以前使用内置的getCollection()
,你现在应该使用自定义的loadAndFormatCollection()
来确保post.slug
是正确的值。
奖励步骤:支持 Jekyll 永久链接
Jekyll 允许您通过在 frontmatter 中指定permalink
键来覆盖任何给定帖子的默认 URL,例如:
--- title : "Hello world!" permalink : "/blog/2013/12/my-special-post/" --- Hello world content!
关于permalink
键,首先要注意的是,它是一个以斜杠开头和结尾的绝对 URL。它还以“博客”开头,这很可能是您收藏的名称。因此,如果您想确认permalink
密钥覆盖默认的博客文章 URL,您需要将这些考虑在内并像这样更新loadAndFormatCollection()
函数:
import { getCollection } from 'astro:content' ; export async function loadAndFormatCollection ( name ) { const posts = await getCollection (name); posts. forEach ( post => { const permalink = post.data.permalink; if (permalink) { const urlParts = permalink. split ( "/" ); urlParts. shift (); // remove first empty space urlParts. shift (); // remove "blog" if (permalink. endsWith ( "/" )) { urlParts. pop (); // remove last empty space } post.slug = urlParts. join ( "/" ); return ; } const date = post.data.pubDate; const year = post. getFullYear (); const month = (post. getMonth () + 1 ). padStart ( 2 , "0" ); post.slug = `${ year }/${ month }/${ post . slug }` ; }); return posts; };
现在,如果帖子的前言中有permalink
,它将优先于默认的 URL 格式。
(注意:如果您正在尝试将 Jekyll 博客转换为 Astro,并且不想完成所有这些手动操作,请查看我的astro-jekyll
项目,它会为您完成所有这些工作。)
结论
这是 Jekyll 默认包含一些功能的另一个例子,Astro 让您跳过几个环节来实现,但最终并没有那么多额外的工作。由于集合项的slug
属性,修改博客文章的 URL 非常简单,同时又不会偏离 Astro 的做事方式太远。毕竟,这就是 Astro 的优势:它完全灵活,您可以让它做任何您想做的事情,只是可能比您习惯于从 Jekyll 获得更多的工作。
原文: https://humanwhocodes.com/blog/2023/03/astro-jekyll-blog-post-url/