静的サイトジェネレータGatsbyを秒速で理解する

[Gatsby](https://www.gatsbyjs.org/)は静的サイトジェネレータフレームワークです。
フロントエンドを作成するときは最近はReactやvueなどのJSフレームワークを使って作成することが多いです。
メリットも大きい反面、webpackやparcelでbundle.jsを作成し、動的に仮想DOMツリーを生成する<b>SPAの欠点</b>として以下のことが挙げられます。

- <b>bundle.jsのファイルサイズが肥大化する（Code Splittingをして適切な分割読み込みや先読みである程度は回避できるが、依存ライブラリのファイルサイズが増えると厳しい）</b>
- <b>そもそも仮想DOMツリーの構築に初回のJS実行時間がかかる（SSRでもバックエンドでJSによる動的にDOM構築をするため、遅い）</b>
- <b>TwitterやFacebookシェアのOGPを実装しようとするとSSRの実装に迫られる（SSRは複雑度を増すのでSPAとしてはあまりやりたくない・・・）</b>

静的サイトジェネレート（JAMStackというらしいです）はこれらの課題を解決する方法の１つです。
具体的にはReactなどの仮想DOMツリーを予めビルドして、htmlファイル（とわずかなJS実装）を生成します。
（ページ単位にhtmlを生成し、GatsbyのLinkコンポーネントを使うことでページ間のリンク構造も保ったまま出力されます）
特にブログやサービスのランディングページなどのパフォーマンスが求められる実装に威力を発揮します。
また、ページ単位にhtmlが出力されるため、OGPタグもページ別に割り当てることができます。
後は出力されたフォルダを丸ごとホスティングするだけで高速なレンダリングが実現できます。

~~個人的にはGatsbyでランディングページを作り、ウェブアプリケーション本体はReactなどのフレームワークで作り、SEO的観点や読み込みパフォーマンス遅延による初回ユーザ離脱防止のため、ランディングページの実装はアプリケーション本体から分離したほうがいいのではないかと最近は考えています。
（SSRしない）~~
2020/02/14追記：reduxやapiコールもできて動的なサイトの作成ができたのでもう全部Gatsbyでいいじゃないかな？

# Gatsbyのインストール
はじめにGastbyのサンプルプロジェクトの作成やGatsbyのビルドを行うため、Gatsbyコマンドをインストールします。

```sh
$ npm install -g gatsby-cli
or
$ yarn global add gatsby-cli
```

# Gatsbyの構成
Gatsbyの構成に関してもうちょっと詳しい説明してくれている記事
参考：[Reactベース静的サイトジェネレータGatsbyの真の力をお見せします](https://qiita.com/uehaj/items/1b7f0a86596353587466)

この記事では理解のため、Gatsbyのサンプルを作成します。

```
$ gatsby new <サイト名> <git clone元>
```

git clone元は未指定の場合、デフォルトで[gatsby-starter-default](https://github.com/gatsbyjs/gatsby-starter-default)からクローンされます。
（なれたら、自分用の雛形をgithubに作っておくと便利かもしれません）

Gatsbyプロジェクトの最低限の構成は次のようになります。
(gatsby-browser.js、gatsby-node.js、gatsby-ssr.jsはオプションなので一旦省略してます）

```
├── gatsby-config.js
├── public
├── src
│   ├── components
│   |   ├── image.js
│   |   ├── layout.css
│   |   ├── layout.js
│   |   └── seo.js
│   ├── images
│   └── pages
│       ├── 404.js
│       └── index.js
└── package.json
```

gatsby-config.jsにはgastbyのビルド設定（OGP情報のテンプレート、ファイル参照設定、プラグインの設定が記載されています）
publicフォルダにはビルド結果が出力されます。
srcフォルダに実装を行います。
pagesフォルダに表示する各ページの実装を行います。
（<b>ファイル名がそのままルーティングのパスとなります。ただしindex.jsのみパスは'/'、ルーティングが存在しない場合は404.jsのファイルに到達する</b>）
imagesフォルダにはデフォルトでページで使う画像ファイル一式を格納します。
componentsには各ページで使うコンポーネントの部品を格納します。

## Gastby関連のパッケージ
package.jsonは次のようになっています。
（必要最低限なライブラリ）

```package.json
  "dependencies": {
    "gatsby": "^2.19.7",
    "gatsby-image": "^2.2.39",
    "gatsby-plugin-manifest": "^2.2.39",
    "gatsby-plugin-offline": "^3.0.32",
    "gatsby-plugin-react-helmet": "^3.1.21",
    "gatsby-plugin-sharp": "^2.4.3",
    "gatsby-source-filesystem": "^2.1.46",
    "gatsby-transformer-sharp": "^2.3.13",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-helmet": "^5.2.1"
  }
```

react, react-domはReactフレームワークでの実装をしているため、使っています。
react-helmetはOGPなどのmetaデータの埋め込みを行うために使用しています。
gatsby-imageは画像、gatsby-plugin-sharp、gatsby-transformer-sharpは画像変換、
gatsby-source-filesystemは画像ファイル取得、
gatsby-plugin-manifestはPWA用manifestファイル生成用、
gatsby-plugin-offlineはServiceWorkerでのオフラインローディング用
でそれぞれ使われています。

## Gastbyのビルド設定（主にプラグインの設定）
gatsby-config.jsにビルド設定が記載されています。
siteMetadataはmetaタグに使う雛形のデータです。（<b>適宜変更する</b>）
pluginsにgatsby関連のプラグイン設定を記述します。
gatsby-source-filesystemプラグインのoptions設定でsrc/imagesフォルダ以下が画像フォルダの参照先に設定されています。
gatsby-plugin-manifestプラグインのoptions設定にPWAのmanifest設定を行います。（<b>適宜変更する</b>）
パラメータの設定は[manifest.json](https://developer.mozilla.org/ja/docs/Web/Manifest)と同じです。

```gatsby-config.js
module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
  ],
}
```

## graphql（useStaticQuery）でのデータリソース取得
サンプルではseo.jsにてOGPの設定を行っています。
Gatsbyはデータ関連の取得はgraphqlを介して行われます。
useStaticQueryでgatsby-config.jsのsiteMetadata設定を取得しています。

```jsx:seo.js
/**
 * SEO component that queries for data with
 *  Gatsby's useStaticQuery React hook
 *
 * See: https://www.gatsbyjs.org/docs/use-static-query/
 */

import React from "react"
import Helmet from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"

function SEO({ description, lang, meta, title }) {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `
  )

  const metaDescription = description || site.siteMetadata.description

  return (
    <Helmet
      htmlAttributes={{
        lang,
      }}
      title={title}
      titleTemplate={`%s | ${site.siteMetadata.title}`}
      meta={[
        {
          name: `description`,
          content: metaDescription,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: metaDescription,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: site.siteMetadata.author,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: metaDescription,
        },
      ].concat(meta)}
    />
  )
}

SEO.defaultProps = {
  lang: `en`,
  meta: [],
  description: ``,
}

export default SEO
```

## 静的画像（ビルド時最適化画像）
image.jsではgatsby-imageで画像ファイルを最適化して取得します。
こちらもgraphqlにてファイル取得し、Sharpで画像変換しています。
本番ビルド時に最適化されて画像リソースが埋め込まれます。

```jsx:image.js
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

/*
 * This component is built using `gatsby-image` to automatically serve optimized
 * images with lazy loading and reduced file sizes. The image is loaded using a
 * `useStaticQuery`, which allows us to load the image from directly within this
 * component, rather than having to pass the image data down from pages.
 *
 * For more information, see the docs:
 * - `gatsby-image`: https://gatsby.dev/gatsby-image
 * - `useStaticQuery`: https://www.gatsbyjs.org/docs/use-static-query/
 */

const Image = () => {
  const data = useStaticQuery(graphql`
    query {
      placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) {
        childImageSharp {
          fluid(maxWidth: 300) {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  `)

  return <Img fluid={data.placeholderImage.childImageSharp.fluid} />
}

export default Image
```

## ページ
index.jsです。`/`ページに該当します。
ページ間の遷移は<b>gatsbyの[Link](https://www.gatsbyjs.org/docs/gatsby-link/)コンポーネント</b>を使います。
ちなみに<b>Reactのstateをコンポーネント内</b>に普通に持てます。（サンプルでは使ってませんが）
ページ遷移はGatsbyのLinkコンポーネントを使います。
（ルーティングはpagesフォルダのファイル名がそのままルーティングされる）

```jsx:index.js
import React from "react"
import { Link } from "gatsby"

import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"

const IndexPage = () => (
  <Layout>
    <SEO title="Home" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
  </Layout>
)

export default IndexPage
```

サンプルでは使われていませんが、DBのデータなどを動的に埋め込む際は
[gatsby-node.js](https://www.gatsbyjs.org/docs/api-files-gatsby-node/)に実装を記述します。
動的なページを生成する際に場合に威力を発揮します。
参考：[GatsbyJS v2 はじめの一歩 (7) 動的にページを生成する](https://mottox2.com/posts/204)



# 開発時
次のコマンドで開発モードで起動できます。

```sh
$ gatsby develop
```

ファイル編集時は自動的にブラウザ上で編集反映(hot reloading)してくれます。（イタレリツクセリ）

# リリースビルド
次のコマンドでリリースビルドが行えます。（minifyや最適化）

```sh
$ gastby build
```

ビルド完了後、publicフォルダに生成されるので丸ごとサーバのpublicフォルダやGitHub Pages、Netlify、FireStorage、S3などにアップロードすればホスティング完了です。
localhostで確認するにはserveコマンドを使う方法があります。

```sh
$ gatsby serve
```

S3にアップロードするのであれば、gatsby-plugin-s3プラグインをつかえばさらに簡単にデプロイできます。
[Deploying to S3/Cloudfront](https://www.gatsbyjs.org/docs/deploying-to-s3-cloudfront/)