Tags:
Astro
Astro で mdx で記述したコンテンツの中身を plain text で取り出したい
(2023/07/16改定)Astro components を含んだ mdx で記述したコンテンツの中身を plain text として表示する方法について。
Astro 初学者が考えた方法なのでもっと簡単な方法がある可能性がある。
環境
- Astro: v2.7.2
- Content Collections 使用
やりたいこと
mdx で Astro Components を含むような記事を書いたとして
src/content/posts/20230701.mdx
---
title: "My First Blog Post"
date: 2022-06-26T20:43:03+09:00
description: "This is the first post of my new Astro blog."
tags: ["astro", "blogging", "learning in public"]
draft: false
---
import Header from "../../components/Header.astro";
# My First Blog Post
<Header />
Welcome to my _new blog_ about learning Astro! Here, I will share my learning journey as I build a new website.
...
記事一覧ページに記事内容の冒頭だけ plain text で表示したい。
方法
Astro components を含んだ mdx であるため、一度 Astro の render 関数を通さないと正確な記事内容が得られないと考えた。
そのため Astro.slots.render()
を使って HTML を生成後、記事内容部分だけ取り出して正規表現で HTML のタグを取り除くことにした。
PlainifyContent コンポーネント
<slot />
で受け取った記事全体の内容を plain text にして表示するコンポーネントを作る。
jsdom を使う。
src/components/PlainifyContent.astro
---
import { JSDOM } from "jsdom";
interface Props {
/**
* 指定の文字数だけ取得して他は捨てる
* @default 300
*/
truncate?: number;
}
const strhtml = await Astro.slots.render("default");
const dom = new JSDOM(strhtml);
const contenthtml = dom.window.document.body.innerHTML;
const { truncate = 300 } = Astro.props;
---
{contenthtml?.replace(/<[^>]+>/g, "").slice(0, truncate)}
参考
記事一覧ページ
記事一覧ページで PlainifyContent を使って記事内容を表示する。
src/pages/posts/[…page].astro
---
import type { GetStaticPathsOptions, Page } from "astro";
import { getCollection, type CollectionEntry } from "astro:content";
import BaseLayout from "../../layouts/BaseLayout.astro";
import PlainifyContent from "../../components/PlainifyContent.astro";
type Props = {
page: Page<CollectionEntry<"posts">>;
};
export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
return paginate(await getCollection("posts"), { pageSize: 2 });
}
const { page } = Astro.props;
const renderedPages = await Promise.all(page.data.map(async (data) => {
return {
...data,
renderedContent: (await data.render()).Content
}
}));
---
<BaseLayout title="記事一覧">
<h1>記事一覧</h1>
<ul>
{
page.data.map((data) => (
<li>
<a href={`/posts/${data.slug}/`}>
<h2>{data.data.title}</h2>
<p>
<PlainifyContent><data.renderedContent /></PlainifyContent>
</p>
</a>
</li>
))
}
</ul>
</BaseLayout>
記事の内容は (await data.render()).Content
で取り出せる。
これを renderedPages[].renderedContent
に格納しておき、
<PlainifyContent><data.renderedContent /></PlainifyContent>
というように PlainifyContent に渡すことによって、PlainifyContent 側で色々やれるようになる。
以上