AstroとWordPressでブログを作ってみるよ
簡単にブログを始める場合、第一候補としてWordPressが挙げられます。WordPressはCMSとして高いシェア率を誇り、様々なテンプレートが存在し、豊富なプラグインも揃っています。
今回はそんなWordPressをHeadless化して、最近話題のAstroフレームワークを使用してブログ構築していきます。
AstroとWordPressを選定した理由
静的サイトとしてファイルを生成するだけならばNext.jsやNuxt.jsでもできるわけです。しかし、それらはアプリケーションに最適化されたフレームワークであり、サイトを作成するといった目的には少しオーバースペックな印象です。ステート管理やルーティングなどの機能はさほど必要ないでしょう。そこで、Astroを選定しました。
Astroには以下の特徴があります。これだけでもサイト制作として最適なフレームワークであることがわかります。
- シンプルな構成であり簡単に取り組める。必要になれば慣れ親しんだReactやVueなどのUIライブラリやフレームワークの記述も使用できる。
- ビルドされたファイルはインタラクティブなコンポーネントを除き、静的なHTMLファイルとして出力されるため、高速な表示が可能。
ではWordPressを選定した理由はなんでしょうか。
WordPressはシェア率が高く、世界中のユーザーが数多く存在します。WordPressのUIに慣れ親しんだユーザーも多く、そのままWordPressを使用したいというユーザーも多いと考えられます。それに加え、Rest APIを使用したHeadless CMSとしても使用できます。WordPressを使用する価値があるでしょう。
実際に作ってみる
それでは実際にAstroとWordPressを使用してブログを作成していきます。
WordPressのインストール
まずはWordPress環境を作ります。ここではボタン操作で簡単にWordPress環境を作成できる「Local」を使用します。
細かい手順は割愛します。ガイドに従って進めていき、このような画面になればOKです。
右上のOpen siteをクリックすると、WordPressで作成したサイトが表示されます。とても簡単に環境構築ができましたね。
Astroセットアップ
次にAstroをインストールします。ドキュメントを見れば簡単にインストールできます。
一通りインストールが完了したら、パスエイリアスを設定します(しなくても問題ありません。ファイル参照をわかりやすくするためのものです)。
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@src/*": ["src/*"]
}
},
}
また、環境変数としてWordPressのAPI_URLを設定します。
API_URL=http://{your-domain}/wp-json/wp/v2
使用する側では以下のように使用できます。
import.meta.env.API_URL
投稿データ取得から表示
投稿データを取得して表示させます。エンドポイントは以下の通りです。
/wp-json/wp/v2/posts
Astroではfetchを使用してデータ取得をします。
トップレベルのawaitを使用することで、非同期処理を同期処理のように記述できます。
index.astro
---
import Layout from '@src/layouts/Layout.astro';
type Post = {
link: string;
title: {
rendered: string;
};
};
const res = await fetch(`${import.meta.env.API_URL}/posts`);
const posts = await res.json() as Post[];
---
<Layout title="投稿一覧">
<main>
<ul>
{posts.map((post) => (
<li>
<a href={post.link} set:html={post.title.rendered} />
</li>
))}
</ul>
</main>
</Layout>
投稿詳細ページを表示
投稿詳細ページを表示させます。エンドポイントは以下の通りです。
/wp-json/wp/v2/posts/{id}
先ほどと同じくfetchを使用してデータ取得をします。
ここで必要なのはgetStaticPaths
関数です。ダイナミックルーティングを使用する場合、事前にパス情報を取得し構築していく必要があるからです。
ここでは一番最初のHello Worldの投稿を取得し、表示しています。
pages/posts/[id].astro
---
import Layout from '@src/layouts/Layout.astro';
type Post = {
link: string;
title: {
rendered: string;
};
content: {
rendered: string;
}
};
export async function getStaticPaths() {
const res = await fetch(`${import.meta.env.API_URL}/posts`);
const posts = await res.json() as { id: number }[];
return posts.map(post => (
{ params: { id: post.id.toString() } }
))
}
const { id } = Astro.params;
const res = await fetch(`${import.meta.env.API_URL}/posts/${id}`);
const post = await res.json() as Post;
---
<Layout title={post.title.rendered}>
<main>
<h1 set:html={post.title.rendered} />
<p set:html={post.content.rendered} />
</main>
</Layout>
カスタム投稿の取得
WordPressではカスタム投稿という機能があります。これは投稿とは別に独自の投稿を作成できます。エンドポイントは以下の通りです。
/wp-json/wp/v2/{custom_post_type}
お知らせカスタム投稿(news)があるとして、その投稿を取得してみます。
news/index.astro
---
import Layout from '@src/layouts/Layout.astro';
type News = {
id: number;
title: {
rendered: string;
};
};
const res = await fetch(`${import.meta.env.API_URL}/news`);
const news = await res.json() as News[];
---
<Layout title="お知らせ一覧">
<main>
<ul>
{news.map((item) => (
<li>
<a href={`/news/${item.id}`} set:html={item.title.rendered} />
</li>
))}
</ul>
</main>
</Layout>
ページネーション実装
先の投稿一覧ページではページネーションがありませんでした。実は投稿エンドポイントのヘッダーにページネーション情報が含まれています。
x-wp-totalpages
これを使用してページネーションを実装してみます。
pages/posts/index.astro
---
import Layout from '@src/layouts/Layout.astro';
import Paginate from '@src/components/Paginate.astro';
type Post = {
id: number;
title: {
rendered: string;
};
};
export async function getStaticPaths() {
const res = await fetch(`${import.meta.env.API_URL}/posts`);
const totalCount = res.headers.get('x-wp-totalpages') as string;
const pages: number[] = Array(+totalCount).fill(null).map((_, i) => i + 1);
return pages.map(page => (
{ params: { id: page.toString() } }
))
}
const { id } = Astro.params;
const res = await fetch(`${import.meta.env.API_URL}/posts?page=${id}`);
const posts = await res.json() as Post[];
const totalCount = res.headers.get('x-wp-totalpages') as string;
const pages: number[] = Array(+totalCount).fill(null).map((_, i) => i + 1);
---
<Layout title={`投稿一覧 | ${id}ページ目`}>
<main>
<ul>
{posts.map((post) => (
<li>
<a href={`/posts/${post.id}`} set:html={post.title.rendered} />
</li>
))}
</ul>
<Paginate pages={pages} currentPage={id} />
</main>
</Layout>
components/Paginate.astro
---
const { pages, currentPage } = Astro.props as { pages: number[], currentPage: string };
---
<style>
nav {
display: flex;
justify-content: center;
}
ul {
display: flex;
list-style: none;
padding: 0;
}
li {
margin: 0 0.5rem;
}
a {
display: grid;
place-items: center;
width: 2em;
height: 2em;
text-decoration: none;
color: #333;
}
.is-active {
pointer-events: none;
background-color: #333;
color: #fff;
}
</style>
<nav>
<ul>
{pages.map((page) => (
<li>
<a class:list={[{ 'is-active': page === +currentPage }]} href={`/posts/page/${page}`}>{page.toString()}</a>
</li>
))}
</ul>
</nav>
ビルドしてファイルを見てみる
ここまでできたらビルドしてみましょう。jsがなく、静的サイトとして出力されていることがわかります。
npm run build
ここからは余談
フォームプラグインとの連携
WordPressのフォームプラグインとしてContactForm7が有名ですが、実はRest APIを使用してフォームの送信ができます。
以下の記事が参考になります。
さらにSEOプラグインとしてYoast SEOが有名ですが、こちらもRest APIを使用してSEO情報を取得できます。
使用するプラグインによりますが、Rest APIが提供されている場合はAstroからも使用できます。
さいごに
ここまでできたら、あとは自由にカスタマイズしていきましょう。そのままastro記法を使用しても良いですし、ReactやVueなどの記法を使うことも使用できます。WordPressのプラグインを活かしてAstroに表示させることもできます。
ビルドされたファイルは静的なHTMLファイルとして出力されるため、高速なサイト表示ができます。
今回は機能としてほんの一握りを紹介しましたが、より多くの機能がAstroにはあります。
ぜひ試してみてください。
参考
Astroの公式ドキュメントは以下を参照にしてください。
基本的なWordPress Rest Apiのエンドポイントは以下を参照にしてください。
Discussion