🚀

AstroとWordPressでブログを作ってみるよ

2023/07/26に公開

はじめに

簡単にブログを始める場合、第一候補として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」を使用します。
https://localwp.com/

細かい手順は割愛します。ガイドに従って進めていき、このような画面になればOKです。
右上のOpen siteをクリックすると、WordPressで作成したサイトが表示されます。とても簡単に環境構築ができましたね。

Astroセットアップ

次にAstroをインストールします。ドキュメントを見れば簡単にインストールできます。
https://docs.astro.build/ja/install/auto/

一通りインストールが完了したら、パスエイリアスを設定します(しなくても問題ありません。ファイル参照をわかりやすくするためのものです)。

tsconfig.json
{
    "extends": "astro/tsconfigs/strict",
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
            "@src/*": ["src/*"]
        }
    },
}

また、環境変数としてWordPressのAPI_URLを設定します。

.env
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を使用してフォームの送信ができます。
以下の記事が参考になります。
https://qiita.com/gungungggun/items/d5736649529e60db50ac

さらにSEOプラグインとしてYoast SEOが有名ですが、こちらもRest APIを使用してSEO情報を取得できます。
https://developer.yoast.com/customization/apis/rest-api/

使用するプラグインによりますが、Rest APIが提供されている場合はAstroからも使用できます。

さいごに

ここまでできたら、あとは自由にカスタマイズしていきましょう。そのままastro記法を使用しても良いですし、ReactやVueなどの記法を使うことも使用できます。WordPressのプラグインを活かしてAstroに表示させることもできます。
ビルドされたファイルは静的なHTMLファイルとして出力されるため、高速なサイト表示ができます。

今回は機能としてほんの一握りを紹介しましたが、より多くの機能がAstroにはあります。
ぜひ試してみてください。

参考

Astroの公式ドキュメントは以下を参照にしてください。
https://docs.astro.build/en/getting-started/

基本的なWordPress Rest Apiのエンドポイントは以下を参照にしてください。
https://developer.wordpress.org/rest-api/reference/

Discussion