skip to content
鰭狀漏斗

在 Astro 擷取 Markdown 文章的摘要做為描述

/ 閱讀時間 3 分鐘

我們常常被要求在 HTML 的 meta description 還有 Open Graph 之類的 metadata 填入一個頁面的描述,常見達成的方法大概有這三個:

  1. 直接沿用網站的描述
  2. 在 frontmatter 加一個 description 欄位,自己寫描述
  3. 從內文中取前幾十字的摘要做描述

方法 1 是最簡單的,不過網站描述通常與文章內容沒什麼關係,所以不太能做為頁面的描述,但如果只是要湊字數的話是可行的。

方法 2 在程式上很簡單,但是要為每個頁面寫出一段話做為描述,真的是有點麻煩的事情。

方法 3 是很多網站的做法,有些 CMS 或 SSG 都有內建這個功能。

要如何在 Astro 實作這個功能呢?

在 Astro 獲得 Markdown 檔案的 excerpt

在 Gatsbyjs,gatsby-plugin-mdx 會產出 excerpt,就直接用這個做為描述就好。

但是如果是 Astro 的話要自己把程式寫出來,幸好有好心人已經寫出來了:銀河渡舟的 rehype-post-excerpt.ts

這個程式很簡單,存取 rehype 產生的 hast(表示 HTML 的抽象語法樹),取得文件的前幾十字放到 frontmatter 中。

放到 frontmatter 中的方法在 Astro 文件中有記載,只要在 file.data.astro.frontmatter 加入自己的 property 即可:

const { frontmatter } = file.data.astro as MarkdownAstroData;
const fragment =
excerpt(tree, {
comment: "more"
}) ??
truncate(tree, {
size: excerptLimit,
ellipsis: "…"
});
frontmatter.excerpt = collapseWhiteSpace(hastToText(fragment));

用這種方式放進去 frontmatter 的 property 有個限制。如果 markdown 檔案是放在 content collection 的話,需要經過 render() 才能得到這個 property:

---
import { getEntry } from 'astro:content';
const blogPost = await getEntry('blog', 'post-1');
const { remarkPluginFrontmatter } = await blogPost.render();
---
<p>{remarkPluginFrontmatter.excerpt}</p>

因為需要 render() 才能得到 property,所以會造成 markdown 檔案 render 的次數增加,從而造成建置時間增加,所以這種動態加入的 frontmatter property 最好謹慎使用。

其實 Gatsbyjs 的 gatsby-plugin-mdx 也是使用類似的方法。它產生 excerpt 的方式是做一次 MDX compile,用在選項加入的 rehype-infer-description-meta plugin 產生 excerpt 再回傳。

不過為什麼在用 Gatsbyjs 的時候沒有感覺到建置時間有被拖長呢?真奇怪呢。

參考資料