• 最終更新日:

WordPressとGatsbyを連携して高速なサイトを作る - 環境構築から基本的なページ作成まで

WordPressとGatsbyを連携して高速なサイトを作る - 環境構築から基本的なページ作成まで

今回はWordPressとGatsbyを連携して高速なサイトの作る手順について説明します。

説明する環境は以下です。

  • macOS Catalina v10.15.5
  • Visual Studio Code v1.57.0
  • node.js v16.13.1
  • react v17.0.1
  • gatsby v4.11.2
この記事の目次

WordPressとGatsby用の環境を作る

WordPressで記事の更新を行い、Gatsby側でそのデータを受け取って実際のサイトに表示させるようにします。それはWordPress用のドメインと、実際のサイトを表示させるためのドメインの2つが必要になるということを覚えておきましょう。

今回は本番環境でサイトを表示させる方法については説明していません。環境構築の手順とGatsbyを使ってWordPressからデータを取得して基本的なページを表示させる方法について説明しています。

まずは環境構築から説明します。

Gatsbyをグローバルにインストール

これからの説明にはnode.jsが必要になります。事前にインストールしておきましょう。
以下の記事が参考になります。

次にGatsbyを使えるように以下のコマンドでグローバルにインストールします。

npm i -g gatsby-cli

WordPressの環境を作る

WordPressの開発環境はLocalで作ります。
Localの使い方は以下の記事を参考にどうぞ。

テーマは「twentytwentyone」を選んでおきます。(twentytwentyだとデータが取得できなかったため) オリジナルテーマがある人はそれでも構いません。

このtwentytwentyoneのテーマで書いた投稿ページや固定ページのデータを、実際に表示させたいサイトのディレクトリにGatsbyを使って取得して、静的ファイルで表示させます。

WordPressにログインしたら以下の2つのプラグインをインストールして有効化しておきましょう。

  • WPGatsby
  • WPGraphQL

Gatsbyを使うディレクトリの準備

Gatsbyをすぐに使えるように公式ではテンプレートがたくさん用意されています。WordPress用のテンプレートもありますが、今回は理解のために基本的なテンプレートを使います。

今回はgatsby-starter-hello-worldのテンプレートを使っていきます。すでにGatsbyを使いたいディレクトリにいるなら以下のコマンドで、そのディレクトリにインストールできます。(Gatsbyで使うディレクトリはWordPressのthemeディレクトリとは別の場所に作りましょう)

gatsby new ./ https://github.com/gatsbyjs/gatsby-starter-hello-world

インストールできると以下のファイルとフォルダが表示されます。

あとはWordPressと連動させるために必要なパッケージと、入れておくと便利なパッケージをインストールしておきます。

npm i -D gatsby-source-wordpress gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp sass gatsby-plugin-sass

それぞれのパッケージのバージョンは以下です。

インストールしたパッケージを有効化するために、gatsby-config.jsに以下のように書きます。

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-wordpress`,
      options: {
        url: `http://hogehoge.wp/graphql`,
      },
    },
    `gatsby-plugin-image`,
    `gatsby-plugin-sharp`,
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sass`,
  ],
}

6行目のoptionsにあるurlには、実際にWordPressで使用するURLを追加して/graphqlとします。これでWordPressが設定しているサイトから情報を取得できるようになります。

sassについて、gatsby自体にautoprefixerが備わっているのでお好みのブラウザなどに変更したい場合はpackage.jsonに追加します。以下は例です。

"browserslist": [
"> 0.2%, not dead",
"not IE 11",
"Safari > 11, iOS > 11"
]

gatsbyのautoprefixについてはこちら

トップページに記事一覧を表示させる

WordPressには投稿ページに3記事ほど足しておきましょう。

パーマネントリンクは分かりやすいようにpost_idにしておきます。

pagesディレクトリの中にあるindex.jsをトップページ用として使います。
スタイルはstylesフォルダを作成して、トップページ専用のp-Home.scssに書いていきます。

記事一覧を表示させるために、index.jsには以下のように書きます。
表示させたい内容は2つです。

  • title…記事のタイトル
  • excerpt…記事の抜粋
import React from "react"
import { graphql } from "gatsby"

import "../styles/p-Home.scss"

const Home = ({ data }) => {
  return (
    <div className="p-Home-main">
      {data.allWpPost.edges.map(({ node }) => (
        <div key={node.id} className="p-Home-block">
          <h2 className="p-Home-heading">{node.title}</h2>
          <div
            className="p-Home-desc"
            dangerouslySetInnerHTML={{ __html: node.excerpt }}
          />
        </div>
      ))}
    </div>
  )
}
export default Home

export const query = graphql`
  query {
    allWpPost(sort: { fields: date, order: DESC }) {
      edges {
        node {
          id
          title
          excerpt
        }
      }
    }
  }
`

それでは説明の前に表示を確認してみます。
以下のコマンドを打って、gatsbyの開発サーバーが立ち上げます。
※LocalでWordPressのサイトをアクティブにしていないとエラーになるので注意

gatsby develop

完全に立ち上がったらhttp://localhost:8000/にアクセスしてみてください。以下のように書いた記事のタイトルと、記事の抜粋が表示されているはずです。

どのようにWordPressからデータを取得したのか?

23〜35行目に書いている以下の部分を見てみましょう。GraphQLを使ってWordPressから以下の3つのデータを取得しています。

  • id … gatsbyがつけた固有のid
  • title … 記事タイトル
  • excerpt … 記事の抜粋
export const query = graphql`
  query {
    allWpPost(sort: { fields: date, order: DESC }) {
      edges {
        node {
          id
          title
          excerpt
        }
      }
    }
  }
`

取得できるデータは以下のurlにアクセスして確かめます。

http://localhost:8000/___graphql

アクセスすると以下の画面になるので、必要なデータにチェックを入れていきます。今回はすべての投稿データを取得するためにallwpPostにチェックを入れてそれぞれのデータを取得しています。

WordPressからGraphQLを通じて、どんなデータが取得できるかは以下の公式をどうぞ。

どのように取得したデータを出力しているのか?

6〜21行目に書いてある以下の部分を見てみましょう。GraphQLから取得したデータを受け取って出力しています。

const Home = ({ data }) => {
  return (
    <div className="p-Home-main">
      {data.allWpPost.edges.map(({ node }) => (
        <div key={node.id} className="p-Home-block">
          <h2 className="p-Home-heading">{node.title}</h2>
          <div
            className="p-Home-desc"
            dangerouslySetInnerHTML={{ __html: node.excerpt }}
          />
        </div>
      ))}
    </div>
  )
}
export default Home

GraphQLで取得したデータを1行目にあるdataで受け取っています。dataをconsole.logで見てみると以下のように取得できていることがわかります。

あとは配列のedgesからmapを使ってループさせて表示させています。

記事一覧ページにサムネイルを表示させる

WordPressに設定したサムネイルを表示させます。サムネイルの設定がされていない場合は、特定の画像を表示させるようにします。

先にWordPress側でアイキャッチ画像を設定しておきましょう。

まずは特定の画像を表示させる設定をします。srcディレクトリの中にimagesフォルダを用意して、サムネイルがないときに表示させる画像を用意しましょう。

srcディレクトリにあるimagesフォルダから画像を取得できるようにgatsby-config.jsに追記します。これでimagesディレクトリから画像を指定できます。

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-wordpress`,
      options: {
        url: `http://gatsbywordpressoriginal.wp/graphql`,
      },
    },
    `gatsby-plugin-image`,
    `gatsby-plugin-sharp`,
    `gatsby-transformer-sharp`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images/`,
      },
    },
    `gatsby-plugin-sass`,
  ],
}

記事一覧ページにサムネイルを表示させたいのでindex.jsに追記します。

import React from "react"
import { graphql } from "gatsby"
import { GatsbyImage, StaticImage } from "gatsby-plugin-image"

import "../styles/p-Home.scss"

const Home = ({ data }) => {
  return (
    <div className="p-Home-main">
      {data.allWpPost.edges.map(({ node }) => (
        <div key={node.id} className="p-Home-block">
          {node.featuredImage ? (
            <GatsbyImage
              image={
                node.featuredImage.node.localFile.childImageSharp
                  .gatsbyImageData
              }
              alt={
                node.featuredImage.node.altText
                  ? node.featuredImage.node.altText
                  : ""
              }
              as={`figure`}
              style={{ aspectRatio: "25/14" }}
            />
          ) : (
            <StaticImage
              src={`../images/img-thumbnail.jpg`}
              alt=""
              as={`figure`}
              style={{ aspectRatio: "25/14" }}
            />
          )}
          <h2 className="p-Home-heading">{node.title}</h2>
          <div
            className="p-Home-desc"
            dangerouslySetInnerHTML={{ __html: node.excerpt }}
          />
        </div>
      ))}
    </div>
  )
}
export default Home

export const query = graphql`
  query {
    allWpPost(sort: { fields: date, order: DESC }) {
      edges {
        node {
          id
          title
          excerpt
          featuredImage {
            node {
              localFile {
                childImageSharp {
                  gatsbyImageData(
                    quality: 100
                    placeholder: BLURRED
                    layout: CONSTRAINED
                    transformOptions: { cropFocus: CENTER }
                    width: 500
                    height: 280
                  )
                }
              }
            }
          }
        }
      }
    }
  }
`

Graphqlからサムネイル画像を取得しているのは以下の部分です。

featuredImage {
  node {
     localFile {
       childImageSharp {
         gatsbyImageData(
            quality: 100
            placeholder: BLURRED
            layout: CONSTRAINED
            transformOptions: { cropFocus: CENTER }
            width: 500
            height: 280
          )
        }
     }
   }
}

gatsbyImageDataで設定できる値は複数ありますが、今回は6つ指定しています。

  • quality … 画像のクオリティ。デフォルトは50
  • placeholder … 画像ロード中の表示方法。デフォルトはdominantColor
  • layout … 画像のサイズの取り扱い。デフォルトはCONSTRAINED。widthとheightで指定したサイズに切り取られ、レスポンシブに対応。そのほかにfixedとFullWidthがある。
  • transformOptions … 画像のトリミング位置や、角度、色味など細かく調整できる。デフォルトはfit: "cover"とcropFocus: "attention"。
  • width / height … 出力される画像サイズ

指定できる値の詳細は以下で確認できます。

取得した値は以下の部分で出力しています。

{node.featuredImage ? (
   <GatsbyImage
      image={
        node.featuredImage.node.localFile.childImageSharp
          .gatsbyImageData
      }
      alt={
        node.featuredImage.node.altText
        ? node.featuredImage.node.altText
        : ""
      }
      as={`figure`}
      style={{ aspectRatio: "25/14" }}
   />
   ) : (
   <StaticImage
      src={`../images/img-thumbnail.jpg`}
      alt=""
      as={`figure`}
      style={{ aspectRatio: "25/14" }}
   />
)}

Graphqlで投稿にfeaturedImage(アイキャッチ画像)があれば<GatsbyImage />が出力されて、無い場合は<StaticImage />が出力されます。

出力する側でも指定できる値がたくさんありますが、今回は以下の2つを指定しています。

  • as … 指定した要素でimgを囲ってくれる。今回はfigureを指定。
  • style … asで指定した要素に適用されるインラインスタイル。今回はaspect-ratioで使用。

表示を確認してみます。
1つはWordPress側でアイキャッチ画像を設定している記事、もう1つはアイキャッチ画像を設定していない記事です。

それぞれ指定通りのサムネイルが表示できているのがわかります。画面を拡大・縮小してもアスペクト比に応じて画像サイズは変更してくれます。

投稿ページを表示させる

記事の詳細ページを表示させるには、pagesディレクトリの中に{WpPost.uri}.jsを作成します。
スタイルはstylesディレクトリの中にp-WpPost.scssに書いていきます。

ファイル名を{WpPost.uri}.jsにすることで、記事詳細ページのurlごとに{WpPost.uri}.jsのファイルがテンプレートとして使われることになります。

詳しくは以下の記事が参考になります。

投稿ページには以下のデータをGraphQLから取得して表示させます。

  • title … 記事タイトル
  • content … 記事の内容
import React from "react"
import { graphql } from "gatsby"

import "../styles/p-WpPost.scss"

const Post = ({ data }) => {
  return (
    <main>
      <h1 className="p-WpPost-heading">{data.wpPost.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: data.wpPost.content }} />
    </main>
  )
}

export const query = graphql`
  query ($id: String!) {
    wpPost(id: { eq: $id }) {
      title
      content
    }
  }
`

export default Post

WordPressからデータを取得している部分は以下です。
wpPostに個別のidをセットして、titleとcontentを取得しています。

export const query = graphql`
  query ($id: String!) {
    wpPost(id: { eq: $id }) {
      title
      content
    }
  }
`

投稿の詳細ページを見るために、記事一覧にある「続きを読む」をクリックしてみましょう。

そうすると記事の詳細を確認することができます。
この記事のURLは13になっていますが、これはWordPress側のパーマネントリンク設定でpost_idに設定しているからです。

固定ページを表示させる

今回のテーマではサンプルの固定記事として1記事公開されているものがあるので、こちらを表示させます。

pagesディレクトリの中に{WpPage.uri}.jsを作成します。
スタイルはstylesディレクトリの中にp-WpPage.scssに書いていきます。

{WpPage.uri}.jsに書く内容は以下です。

import React from "react"
import { graphql } from "gatsby"

import "../styles/p-WpPage.scss"

const Page = ({ data }) => {
  return (
    <main>
      <h1 className="p-WpPage-heading">{data.wpPage.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: data.wpPage.content }} />
    </main>
  )
}

export const query = graphql`
  query ($id: String!) {
    wpPage(id: { eq: $id }) {
      title
      content
    }
  }
`

export default Page

投稿ページのときは大きく違うところは、17行目がwpPostからwpPageに変わったことですね。これもhttp://localhost:8000/___graphqlにアクセスしてどのデータが取れるか確認できます。

それでは実際にこの固定ページにアクセスしてみましょう。
この固定ページのURLを調べる場合はGraphqlからwpPageのuriにチェック入れると分かります。

http://localhost:8000/sample-pageにアクセスしてみると以下のように固定ページのタイトルと内容が表示できていることが分かります。

WordPressに必要な最低限のCSSを準備する

WordPressで使用されている必要最低限のCSSを読み込ませます。WordPressの中にある以下のディレクトリからstyle.cssとtheme.cssを取得します。

wp-includes > css > dist > block-library

取得できたら以下のように@wordpressディレクトリを用意してその中に配置させます。

あとは{WpPage.uri}.jsや{WpPost.uri}.jsに読み込ませれば準備完了です。

import "../@wordpress/block-library/build-style/style.css"
import "../@wordpress/block-library/build-style/theme.css"

読み込んだCSSが適用されているか確認してみます。
WordPressの投稿画面で、【メディアとテキスト】のブロックを配置してみます。

次にhttp://localhost:8000/の投稿ページで反映されているか確認してみます。

ちゃんと画像とテキストが左右に配置されているので、CSSが適用できていることがわかります。

注意点としてはこのCSSはあくまでWordPressの大枠のレイアウトを指定しているだけなので、すべてのスタイルが適用できているわけではありません。足りない指定については自分のCSS側で調整する必要があります。

ヘッダーを作成する

headerに載せたい情報は以下の2つを想定します。

  • サイトのタイトル
  • グローバルメニュー

まずは新しくcomponentsフォルダを作成して、その中にheader.jsを作ります。

サイトタイトルを表示させる

WordPress側で設定したサイトのタイトルを取得するためにはheader.jsに以下のように書きます。

import React from "react"
import { Link, useStaticQuery, graphql } from "gatsby"

export const Header = () => {
  const {
    wp: {
      generalSettings: { title },
    },
  } = useStaticQuery(graphql`
    query HeaderQuery {
      wp {
        generalSettings {
          title
        }
      }
    }
  `)

  return (
    <header className="l-header">
      <h1 className="l-header__heading">
        <Link to="/">{title}</Link>
      </h1>
    </header>
  )
}

ページ用のコンポーネント以外でGraphqlでWordPressからデータを取得する場合は9行目にあるuseStaticQueryを使います。

WordPressのサイトタイトルはwpの中のgeneralSettingsにあります。その中からtitleを抜き出して出力しています。

作成したheader.jsをpageコンポーネントで読み込んで表示させてみます。
{WpPost.uri}.jsにheader.jsを読み込ませてみましょう。

import React from "react"
import { graphql } from "gatsby"

import "../@wordpress/block-library/build-style/style.css"
import "../@wordpress/block-library/build-style/theme.css"

import "../styles/p-WpPost.scss"

import { Header } from "../components/header"

const Post = ({ data }) => {
  return (
    <>
      <Header />
      <main>
        <h1 className="p-WpPost-heading">{data.wpPost.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: data.wpPost.content }} />
      </main>
    </>
  )
}

export const query = graphql`
  query ($id: String!) {
    wpPost(id: { eq: $id }) {
      title
      content
    }
  }
`

export default Post

投稿ページの表示を確認してみます。
サイトタイトルが表示されているのがわかります。

メニューを表示させる

WordPress側でheaderに表示させるメニューの設定をする必要があります。
以下の手順に従ってメニューを設定してみましょう。

今回は3つの投稿ページ、それから1つの固定ページをメニューとして表示させます。

まずがcomponentsディレクトリの中にmenu.jsを作成しましょう。

作成したmenu.jsに以下を書きます。

import React from "react"
import { Link, useStaticQuery, graphql } from "gatsby"

export const Menu = () => {
  const {
    allWpMenu: {
      nodes: [
        {
          menuItems: { nodes },
        },
      ],
    },
  } = useStaticQuery(graphql`
    query MenuQuery {
      allWpMenu(filter: { name: { eq: "ヘッダーメニュー" } }) {
        nodes {
          menuItems {
            nodes {
              url
              label
              id
            }
          }
        }
      }
    }
  `)

  return (
    <nav className="p-gnav">
      <ul className="p-gnav__list">
        {nodes.map(node => (
          <li key={node.id} className="p-gnav__item">
            <Link to={node.url}>{node.label}</Link>
          </li>
        ))}
      </ul>
    </nav>
  )
}

GraphqlではallWpMenuでWordPressで設定したすべてのメニューを取得できます。今回はWordPress側で「ヘッダーメニュー」という名前で作成したので、その名前でフィルターをかけてそれぞれのデータを取得しています。

取得できたらmapを使ってそれぞれのURLと名前を出力しています。
表示を確認するため、menu.jsをheader.jsに読み込ませてみましょう。

import React from "react"
import { Link, useStaticQuery, graphql } from "gatsby"
import { Menu } from "./menu"

export const Header = () => {
  const {
    wp: {
      generalSettings: { title },
    },
  } = useStaticQuery(graphql`
    query HeaderQuery {
      wp {
        generalSettings {
          title
        }
      }
    }
  `)

  return (
    <header className="l-header">
      <h1 className="l-header__heading">
        <Link to="/">{title}</Link>
      </h1>
      <Menu />
    </header>
  )
}

読み込めたら投稿ページの表示を確認してみます。
以下のように「ヘッダーメニュー」で作成したものが表示できているのがわかります。

404ページを作成する

アクセスしたURLにページが存在しない場合、404ページを表示させます。
表示のさせ方はpagesディレクトリに404.jsを作成するだけです。

あとは表示させたい内容を書きましょう。
以下は例です。

import React from "react"

const NotFoundPage = () => {
  return (
    <>
      <h1>このページは404ページです</h1>
      <p>アクセスしたURLに記事はありません</p>
    </>
  )
}

export default NotFoundPage

適当なURLでアクセスしてみます。
試しにhttp://localhost:8000/100/と存在しないURLでアクセスしてみると以下の表示になります。

本番環境ではこのような表示になりません。実際の表示を確認する場合は、【Preview custom 404 page】をクリックします。

実際の404ページは以下のように表示されます。

WordPressの投稿画面とGatsby側の表示を同じにするには?

実際の表示はGatsby側のドメインで行うので、WordPress側の投稿画面と見た目が違います。WordPress側の投稿画面をGatsby側の実際の表示と合わせるには以下の記事を参考にしてみてください。

エラーで表示できない場合の対処法

ファイル名を変えたり、ディレクトリ構造を変更したりなど、何か修正したタイミングでターミナル上でエラーが起きて先に進めない場合があります。

Gatsbyが出力する.casheが影響していることが多いので以下のコマンドで1度.casheとpublicフォルダを削除してみてください。

gatsby clean

削除できたらもう1度以下のコマンドでgatsbyを動かすと解決することが多いです。

gatsby develop

まとめ

今回はWordPressとGatsbyで高速なサイトを作る流れについて説明しました。WordPressを使って記事を書き、Gatsby側でそのデータを使って静的サイトを作成することができます。

メリットとデメリットは以下。

メリット

  • SSGで静的サイトを表示させるため、表示が超高速
  • サーバーを経由しないのでセキュリティに強い

デメリット

  • WordPressの投稿用ドメインと、Gatsbyで作成した表示用のドメインの用の2つが必要
  • Gatsby側とWordpress側の両方の管理が必要
  • ページ数が膨大だとビルド処理に時間がかかる

WordPressをGatsbyで静的サイトにするには、まだページネーションの実装やheadの中のmetaの設定、本番環境へのアップする作業が残っているので、今後説明していく予定です。

2020.05.02追記
headのtilteやmeta情報をページごとに設定する方法については以下をどうぞ。