爆速静的サイトジェネレーターのGatsbyJs入門

February 16, 2019
当ブログでも使用されているReact用の静的サイトジェネレータGatsbyJsのコンセプト、技術的な仕組みや、使い方について紹介します。

GatsbyJsとは

GatsbyJsは、React用に作られたモダンで高速なサイトを作成できる静的サイトジェネレータです。

当ブログもGatsbyJsで構築されていますが、GatsbyJsのサイトのトップページに書かれている様に、驚くほど簡単に爆速モダンWebサイトを構築できます。

Build modern, beautiful, secure, blazing fast, apps and websites with React.

そんなGatsbyJsは大きく分けると以下の4つのコンセプトに基づいて実装されています。

  • モダンなWeb技術を手軽に
  • 超絶パフォーマンス
  • 豊富なデータソースへの対応
  • 安価でスケーラブル・セキュアなサイト運用

GatsbyJsの魅力を十分に伝えるために、まずはそれぞれのコンセプトについて説明します。

モダンなWeb技術を手軽に

React.jsWebpackBabel、様々なJavascript・CSS便利なライブラリなど、モダンWeb開発の技術は次々に登場していますが、それらをすべて自前で構築するのは苦痛を伴います

GatsbyJsでは、gatsby newとコマンドを実行すれば、React・babel・webpack等のモダンWeb開発環境がセットアップ済みのプロジェクトを作成することが出来ます。このあたりはFacebook公式のcreate-react-appのような使い心地です。

またTypescriptstyled-componentsなどの言語・ライブラリを導入する際も、GatsbyJsはプラグインをインストールし、簡単な設定を書くだけで実現することが出来ます。

さらに、gatsby-starter-typescript等のように、必要なプラグインがすでにセットアップされたスターターをプロジェクトを作成時に指定することもできます。(公開されているプラグインやスターター一覧)

もちろんWebpackやbabelの設定を自分でカスタマイズすることも可能なので、環境を構築したあとに状況変化に柔軟に対応することが出来ます。

この様に、モダンなWeb技術を手軽にためすことができるのはGatsbyJsの強みの一つです。

超絶パフォーマンス

GatsbyJsの超絶パフォーマンスは大きくわけると

  • 静的ページの事前ビルド
  • SPA
  • SSR
  • PWA

の4つの工夫によって実現されています。

静的ページの事前ビルド

GatsbyJsは静的サイトジェネレータの為、Wordpress等の既存のCMSの様にページ要求に応じてページを作成するのではなく、ページを事前に構築してそれをWebサーバーにホストすることで、ユーザーからの要求に対して迅速にページを配信することが出来ます。

SPA

GatsbyJsでビルドされたページは、SPA(Single Page Application)として動作します。Wordpress等で作成されたページでは、遷移時にページ全体のHTMLが書き換わりますが、SPAのページはJavascriptを用いてページ内のHTMLを部分的に差し替えてコンテンツを切り替える為、高速な画面遷移を実現できます。

SSR

SPAでページを構築する場合ページ全体がJavascriptで記述されている為、初回のページローディングにかかる時間が増えるというデメリットがありますが、GatsbyJsはビルドする際にSSR(Server Side Rendering)するため、ユーザーにはすでにJavascriptが実行済みのHTMLを返却することができるので、初回ローディングのパフォーマンス劣化を防いでいます。また、SEO的にもSSRされている方が安心できるのも嬉しいですね。

PWA

Gatsby.jsはGoogleが提唱するPRPLというProgressive Web App(PWA)を構築・配信するための実装パターンを採用しています。PRPLとは以下の言葉を表しています。

  • Push : を使用して初期URLルートに不可欠なりソースをプッシュする
  • Render : 最初のルートをレンダリングする
  • Pre-Cache : 残りのルートを事前キャッシュする
  • Lazy-load : オンデマンドで残りのルートを遅延読み込みする

簡単に言うと、GatsbyJsがビルドするページは初回ロード時に重要なHTML, アセット類のみをローディングするため初回ローディングスピードが速く、一度ロードされると他のページのリソースをプリフェッチする為ページ遷移が爆速になるということです。

上述の4つの工夫によって、GatsbyJsでWebサイトを作ると誰がどう作っても超絶パフォーマンスなサイトを作成出来ます

豊富なデータソースへの対応

GatsbyJsはプラグインを利用することで、1つまたは複数のデータソースから必要なデータを使ってWebサイトを構築出来ます。データはGraphQLを介して取得する事ができ、対象とするデータソースは、ヘッドレスCMS、Sassサービス、API、DB、マークダウン等のファイルシステム等幅広く対応しています。

例えば、Wordpressでサイトを運用していたがGatsbyJsでページを作り直したいという状況であれば、gatsby-source-wordpressというプラグインを使って、WordPress REST APIからページ構築のために必要なデータを取得し、ページのテンプレートはReactで記述して、GatsbyJsで静的ページをビルドする等の構成も簡単に実現することが出来ます。

安価でスケーラブル・セキュアなサイト運用

これはGatsbyJsというよりも静的サイトジェネレータ全般の特性ですが、データベースやアプリケーションサーバーなど、複雑なインフラ構成を要せず、ホスティングサーバーに事前ビルドしたファイルを配置しておけばいいだけなので、インフラ構築・メンテナンスコスト・サイトスケーリング・セキュリティについて殆ど気にすることがありません。個人で運営するサイトや、コーポレートサイトなどの様に専任の開発担当がいない状況において非常にありがたい特徴です。

GatsbyJsの使い方

GatsbyJsの実際の使い方を、マークダウンをデータソースとしたブログ記事ページの作成過程を具体例として説明しようと思います。

Gatsbyプロジェクトの作成

Gatsbyプロジェクトの作成・ビルドなどはGatsby CLIを使用します。Gatsby CLIはnpmパケージとして公開されており、Gatsbyプロジェクトの作成をする際はnpx経由で以下のようにコマンドを実行します。

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

gatsby new [rootPath] [starter]コマンドは、rootPathにプロジェクトのルートディレクトリ名、starterにプロジェクトの雛形となるスタータープロジェクトのURLを指定することで、Gatsbyプロジェクトを作成するコマンドです。

作成されたGatsbyプロジェクトの構成は以下の様になります。各ディレクトリの説明は公式ドキュメントを参照してください。

Gatsby new Project

次に作成したGatsbyプロジェクトに移動して、以下のコマンドを実行することで、Gatsbyプロジェクトがビルドされた開発サーバーが立ち上がります。

cd blog
npm run development

そして、ブラウザでhttp://localhost:8000にアクセスすればビルドされたページが表示されます。

gatsby dev server

このnpm run developコマンドは、npm scriptとしてgatsby developというGatsby CLIのコマンドが実行されるように定義されてあります。

"scipts": {
  "develop": "gatsby develop"
}

Gatsby CLIの各種コマンドのより詳しい説明は公式ドキュメントをみるか、npx gatsby -hで確認することが出来ます。

ページの作成

無事Gatsbyプロジェクトを作成することが出来たので、次はページの作成方法について説明します。

Gatsbyプロジェクトでページの作成する方法は大きくわけると2つあります。

  • src/pagesディレクトリ内にReactコンポーネントを作成する
  • GatsbyAPIを利用してプログラマブルにページを作成する

src/pagesディレクトリ内にReactコンポーネントを作成する

先程作成したGatsbyプロジェクトのsrc/pagesディレクトリに以下の様にabout.jsというファイルを追加します。

// src/pages/about.js

import React from 'react'

const About = () => (
  <div>
    <h1>About Page</h1>
    <div>This is a About Page</div>
  </div>
)

export default About

そして、ブラウザからlocalhost:8000/aboutにアクセスすれば以下のページが表示されます。

Aboutページ

この様に、GatsbyJsはビルド時にsrc/pagesディレクトリにあるReactコンポーネントをファイル名に対応するパスのページに変換してくれるのです。

GatsbyAPIを利用してプログラマブルにページを作成する

先程のやり方でWebサイトの全てのページを作成してもよいのですが、ブログを作成するとなると、すべてのコンテンツページ用のReactコンポーネントを作成するわけにはいきません。そのため、GatsbyJsではテンプレートとなるReactコンポーネントを指定してプログラマブルにページを作成するcreatePagesというAPIを用意しています。

まずはじめに、プロジェクトのルートディレクトリ直下にgatsby-node.jsというファイルを以下の様に作成します。

// gatsby-node.js
const path = require("path");
const blogs = [
  {
    path: "blogs/1",
    date: "2019/02/15",
    title: "My first blog post"
  },
  {
    path: "blogs/2",
    date: "2019/02/16",
    title: "My second blog post"
  }
];

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions;
  return new Promise((resolve, reject) => {
    blogs.forEach(blog =>
      createPage({
        // ページのパス名
        path: blog.path,
        // テンプレートとなるコンポーネントの指定
        component: path.resolve(`src/templates/blog-template.js`),
        // テンプレートとなるコンポーネントに渡す変数
        context: {
          path: blog.path,
          date: blog.date,
          title: blog.title
        }
      })
    );
    resolve();
  });
};

次に、テンプレートに指定するコンポーネントをsrc/templates/blog-template.jsに作成します。

// src/templates/blog-template.js
import React from 'react'

const BlogTemplate = ({ pageContext: { title, path, date } }) => (
  <div>
    <h1>{`${title} Page`}</h1>
    <div>date : {date}</div>
  </div>
)

export default BlogTemplate

そして、npm run developで開発サーバーを立ち上げて、ブラウザからlocalhost:8000/blogs/1にアクセスすると、以下のページが表示されるはずです。

Blog page

この時点ではブログのデータを定数で用意してますが、次の節でGraphQL経由でブログのデータを取得し、そのデータをもとにページを作成できるようにします。

ページコンポーネントへのデータの受け渡し

GatsbyJsを使ってページを作成する方法を説明したので、次はGraphQL経由でマークダウンから記事の取得し、取得したデータを使ってページを構築する方法について説明します。

また、今回はGraphQLについては詳しく説明しませんので、詳細が気になる方は公式ドキュメントを確認してください。

マークダウンからデータを取得できるようにする

はじめにマークダウンからデータを取得できるようにするためにgatsby-source-filesystemgatsby-transformer-remarkというプラグインをインストールします。

npm install --save gatsby-source-filesystem gatsby-transformer-remark

gatsby-source-filesystemはGatsbyからファイルシステムを読み込めるようにするために、gatsby-transformer-remarkはマークダウンファイルのパースをするために必要なプラグインです。

次にプラグインの設定を有効化するため、gatsby-config.jsファイルをプロジェクトのルートディレクトリに作成します。

// gatsby-config.js

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/markdowns`,
        name: "markdown-pages",
      }
    },
    `gatsby-transformer-remark`,
  ]
}

これでマークダウンを使用する準備はできたので、次はデータソースにするマークダウンファイルをsrc/markdownsディレクトリ内に作成します。ファイルの先頭に追加したブロック内の情報は、gatsby-transformer-remarkによってパースされてGraphQL経由で取得できるようになります。

// src/markdowns/my-first-post.md
---
path: "blogs/1"
date: "2019/02/15"
title: "My first blog post"
---

# My First Blog Post
texttexttexttexttexttexttexttexttext
// src/markdowns/my-second-post.md
---
path: "blogs/2"
date: "2019/02/17"
title: "My second blog post"
---

# My Second Blog Post
texttexttexttexttexttexttexttexttext

マークダウンをデータソースにページを作成する

マークダウンをデータソースにするための設定が完了したので、次はマークダウンの記事データからページを作成できるようにします。

gatsby-node.jsのソースコードを以下の様に書き直しました。graphqlを使ってマークダウンファイルのデータを取得し、そのデータを使ってcreatePage`でページを作成しています。

//gatsby-node.js

const path = require('path')

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  return graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            frontmatter {
              path
              date
              title
            }
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) return Promise.reject(result.errors)

    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
      createPage({
        path: node.frontmatter.path,
        component: path.resolve(`src/templates/blog-template.js`),
        context: {
          path: node.frontmatter.path,
          date: node.frontmatter.date,
          title: node.frontmatter.title,
        }
      })
    })
  })
}

ここで再度npm run developで再ビルドし、ブラウザでlocalhost:8000/blogs/1にアクセスすると、ちゃんとマークダウンのデータからページが作成されていることが確認できます。

ページコンポーネント内でGraphQLのスキーマを定義する

マークダウンのデータからページを作ることができるようになりましたが、このままだとマークダウンの項目が追加されたり変更された場合に、gatsby-node.jsやコンポーネントのファイルなどあちこち修正しなければいけないのと、createPagesAPIを使ってページを作成しないとGraphQL経由でデータが取得出来ないため困ってしまいます。

GatsbyJsでは、ページ・テンプレートのコンポーネントのファイル内でGraphQLのスキーマの定義、データの受け渡しができる仕組みが用意されているので、その仕組を使って書き直します。

まずgatsby-node.jsのcontextで渡す変数としてpathだけを渡すようにします

// gatsby-node.js

exports.createPages = ({ graphql, actions }) => {
  // 全体的に省略
  context: {
    path: node.frontmatter.path
  }
}

そして、src/templates/blog-template.jsのコードを以下の様に書き直します。

// src/templates/blog-template.js

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

const BlogTemplate = ({ data }) => {
  const {
    markdownRemark: {
      html,
      frontmatter: {
        title,
        date
      }
    }
  } = data
  return (
    <div>
      <h1>{`${title} Page`}</h1>
      <div>date : {date}</div>
      <div dangerouslySetInnerHTML={{ __html: html }} />
    </div>
  )
}

export default BlogTemplate

export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date
        title
      }
    }
  }
`

マークダウンデータを取得するためのGraphQLのクエリがファイルの後半で記述されています。

export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date
        title
      }
    }
  }
`

Gatsbyはこのクエリの結果を、BlogTemplateのpropsにdataプロパティとして注入します。

const BlogTemplate = ({ data }) => {
  const {
    markdownRemark: {
      html,
      frontmatter: {
        title,
        date
      }
    }
  } = data
  // 省略

そして、再度npm run developし直し、ブラウザでアクセスlocalhost:8000/blogs/1にアクセスすると、正しくブログが表示されているのと、マークダウンで記述した本文もちゃんと表示されていることが確認できます。

blog page complete

これで、GatsbyJsを使ってマークダウンからブログページを作成することが出来ました。

同じような要領で、src/pages/index.jsでGraphQLのスキーマを記述して、ブログ一覧を表示させるなども簡単に実現できるようになります。

GatsbyJsが向いている用途

GatsbyJsはWordpressで構築しているサイトの用途であれば同様かそれ以上に向いているのではと思います。

例えば

  • ブログ
  • コーポレート
  • LP
  • ECサイト
  • ドキュメンテーションサイト

など様々な利用シーンが考えられます。GatsbyJsを採用しているサービスのリストが公式に公開されているのでそれを見ると利用用途がイメージしやすいかもしれません。

一方、事前ビルドに時間がかかりすぎてしまうためページ数が数万にもなるような大規模サイトや、認証を伴う会員制サービス等はあまり向いていないと感じます

認証機能を伴うサイト構築のチュートリアルが公式で公開されてはいますが、やはり用途は限定されてしまうのではというのと、それならNext.jsでアプリケーションを構築したほうが楽なのでは?と個人的には思います。

おわりに

GatsbyJsはモダンで高速なWebサービスを簡単に構築するということに特化しており、普段Reactを書いている人が自分の個人のサイトを作ったり、コーポレートサイトを作ってくれと言われたときには非常に有力な選択肢になると思います。

また、使われている技術も非常に先進的なので、GraphQLやPWA、SSRなどGatsbyJsを使う中で様々な技術の学習をする事ができるというのもいいポイントです。

わかりやすいチュートリアルが公開されている事とexampleサイトも多数公開されているので、Webフロントエンドがそこまで詳しくない方も安心して挑戦できると思いますので、本記事でGatsbyJsに興味を持った方はぜひとも試してみてください。