Astro コンテンツコレクションへの移行
UI開発者 菅原ミツエーリンクスのウェブサイトをリニューアルして数カ月経ちました。テックラジオでもお話したとおり、フレームワークにはAstroを使用しています。
リニューアル後も、作業効率や表示パフォーマンスの向上を目的として、コードの改善などに継続的に取り組んでいます。
最近行っているのは「コンテンツコレクション」への移行です。htmlファイルを出力する時間が短縮できると聞いて、移行することになりました。
この記事ではコンテンツコレクションの概要と、移行した結果についてご紹介します。
コンテンツコレクションとは
Astroに用意された、コンテンツ管理に適した機能です。src/content
配下にディレクトリを作り、それを1つのまとまり(コレクション)として管理できます。
src/content/column
: コラムコレクションsrc/content/news
: お知らせコレクション
のように使用します。
コレクションのコンテンツとして使用できるファイル形式はMDXやマークダウン、JSONなどです。
使い方
当社では現在、
の3つのコンテンツをコンテンツコレクションで構築しています。
そのうちの1つ、ニュースを例にとってコンテンツコレクションの使い方を説明します。
次の画像は、コンテンツコレクションを使用した場合のディレクトリ構造を簡略化したものです。src/content/
にコンテンツとなる.mdxファイルを格納し、src/pages/
にテンプレートとなる.astroファイルを格納しています。
そのファイルをビルドしたものがdist
フォルダーに.htmlファイルとして出力されます。
次項より、詳細を説明します。
1. コレクションの定義
コンテンツを作成する前にsrc/content/
配下にconfig.ts
を作成し、各コレクションに必要なデータの型を定義します。
ミツエーリンクスのウェブサイトに掲載するお知らせの詳細ページには本文のほかに以下のようなメタデータが必要です。
- 記事のタイトル
- 公開日
- OGP画像のファイルパス
また、なかには「最新のお知らせ一覧には表示しないお知らせページ」や「お知らせ一覧にだけ表示してページとして生成しない(外部リンクのみ)お知らせ」などもあるため、
- 外部リンク
- 最新のお知らせ一覧に表示するかどうかを示すフラグ
もオプショナルとして定義します。
これらをコードとして定義すると以下のようになります。
import {z, defineCollection} from 'astro:content';
const news = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
date: z.object({
year: z.string(),
month: z.string(),
day: z.string()
}),
ogImage: z.string().optional(),
externalLink: z.string().optional(),
latest: z.boolean().optional()
})
});
export const collections = {
news
};
コンテンツコレクションではZodを使って型の管理をしています。
これによりデータが足りない状態、データの型を誤った状態でビルドするとエラーが発生するため、安全に運用ができます。
2. コンテンツファイルをつくる
src/content/
配下にnews
ディレクトリを作り、MDXファイルを追加します。
例えば「LinkedIn Talent Awards 2024においてラーニングチャンピオンのWinnerに選出」というお知らせページの場合、src/content/news/20240823.mdx
というファイルを作成し、以下のように記述します。
---
title: 'LinkedIn Talent Awards 2024においてラーニングチャンピオンのWinnerに選出'
date:
year: '2024'
month: '08'
day: '23'
ogImage: '20240823_news.jpg'
---
import Media from '@components/module/media/media.astro';
import Caption from '@components/module/media/media_caption.astro';
[LinkedIn](https://www.linkedin.com/)が主催する[Talent Awards 2024](https://business.linkedin.com/talent-solutions/events/24/06/talent-awards-apac/region-categories/apac/japan)において、[ラーニングチャンピオンのWinner](https://business.linkedin.com/talent-solutions/events/24/06/talent-awards-apac/region-categories/apac/japan/japan-learning-champion)に選出されました(日本国内で従業員数1万人未満の企業部門)。
ラーニングチャンピオンとは、従業員を適切かつ応用可能なスキルと結びつけることで、従業員の学習と能力開発に投資した企業を表彰するものです。
今回の受賞は、社員一人ひとりの成長と取り組みの成果を示すものだと考えています。[LiniedInラーニング](https://www.linkedin.com/learning/)の活用で、社員の成長を支援できること、そして社員それぞれの役割において力が発揮できることを大変嬉しく思います。
更なる挑戦と成長を続け、より高い目標に邁進してまいります。
<Media alt="" height="533" src="/news/img/20240823_01.webp" width="800">
<Caption slot="caption">授賞したトロフィ-</Caption>
</Media>
記事のメタデータはFrontmatter内で定義します。定義するデータは「1. コレクションの定義」で定義した型にマッチする必要があります。config.ts
で.optional()
がついているものは、ここに書かれていなくてもエラーになりません。
基本的な文章はマークダウン記法で書きますが、独自に作成したAstroコンポーネントをimport
して利用することもでき、Media
やCaption
タグはimportしたAstroコンポーネントを使用しています。
また、プレーンなマークダウンで書いた箇所はクラス属性がないHTMLタグに変換されるので、スタイルを当てたい場合、別途CSSの追加が必要です。
3. テンプレートファイルを作る
MDXファイルをhtmlに変換するには、テンプレートとなるAstroファイルが必要です。
テンプレートファイルはsrc/pages/
配下に格納し、ファイル名は[slug].astro
または[...slug].astro
のようにつけます。このslug
については固定ではなく、任意の名前もつけられます。
お知らせのテンプレート(src/pages/news/[...slug].astro
)は以下のようになっています。
---
import {getCollection} from 'astro:content';
export async function getStaticPaths() {
const newsEntries = await getCollection('news');
return newsEntries.map(entry => ({
params: {slug: newsEntries.slug},
props: {entry}
}));
}
const {entry} = Astro.props;
const {Content} = await entry.render();
const {title, date} = entry.data;
---
<PageTitle>
<Level1Column datetime={`${date.year}-${date.month}-${date.day}`}>
<Fragment slot="heading"><Fragment set:html={title} /></Fragment></Level1Column>
</PageTitle>
<Content />
ポイントは以下の通りです。
getStaticPaths
メソッドの返り値のうち、params
で指定した値が最終的に出力されるHTMLのファイル名になり、props
で指定した値がAstro.props
から取得できるデータになります。Astro.props
のentry.data
には「2. コンテンツファイルをつくる」でFrontmatter内に書いたデータが入っています。テンプレートファイルで使用するtitle
とdate
を取り出します。<Content />
にはMDXファイル内のFrontmatterの外に書いたコンテンツがHTMLに変換された状態で挿入されます。
以上がコンテンツコレクションでの記事作成に必要な作業を抜粋したものになります。実際の記事へのリンクはこちらです。
Astro コンテンツコレクションに移行してみて
当初の移行理由のhtmlファイル出力の時間短縮ですが、コンテンツコレクション導入前後、それぞれ5回ファイルを出力した際の数値を比べると以下のようになりました。
build時間の平均 | build時間の中央値 | 1ページあたり | |
---|---|---|---|
変更前 | 527.95秒 | 538.14秒 | 134.82ミリ秒 |
コンテンツコレクション 導入後 |
328.56秒 | 328.37秒 | 82.91ミリ秒 |
導入前後で約4割ほどビルド時間が短縮されています。現在Astroで管理しているページ数は約4000ページほどあり、今後もページ数が増えていくのでうれしい結果となりました。
そのほかに、コンテンツファイルを追加・ビルドすれば以下の一覧ページも自動で書き換わるようにしました。
一覧ページの更新時に手動で更新する箇所が減ったので、それに伴い記事詳細と記事一覧でタイトルや日付が一致していない、記事へのリンクが違うなどのミスが減りました。
まとめ
Astroはアップデートが頻繁にあり、今後の機能追加も楽しみにしながら随時改善に努めたいと思います。
以上、Astro コンテンツコレクションへの移行についてのフロントエンドBlogでした。