tech blog技術メモ

Web制作に関連する技術を発信しています。

Gatsby v2.26のアップデートを確認する

今更すぎるのですが、先日Gatsbyをv2.26にアップデートするにあたり、変更点を確認したので自身の理解のためにも記事に残しておきます。変更点の全容は公式のリリースノートをご確認ください。

今回はその中でも特に気になった以下のアップデートについて書いておきたいと思います。

  • File System Route API
  • gatsby-plugin-image

File System Route API

File System Route APIは同レイアウトでコンテンツが異なるページの生成が簡単にできるAPIです。代表的な例としてブログ記事を思い浮かべるとわかりやすいかなと思います。Next.jsやNuxt.jsなどのフレームワークではお馴染みの機能ですが、Gatsbyでも同様の機能が搭載されました。

これまではgatsby-node.jscreatePagesAPIを使用して生成していましたが、File System Route APIは命名規則に則ったファイル名を設定することで、createPagesAPIを内部的に呼び出し、抽象化を実現したとのことです。

Announcing Gatsby’s new File System Route API

File System Route API以前のページ生成方法

File System Route API以前の同レイアウトのページの生成方法は、gatsby-node.jscreatePagesAPIを使用してGraphQLのクエリからデータを取得していました。

例としてマークダウンからコンテンツを取得してページを生成する方法を以下に記します。

gatsby-node.js
exports.createPages = ({ actions, graphql }) => {
  const { createPage } = actions
  const postTemplate = path.resolve(`src/templates/post.js`)

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

    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
      createPage({
        path: node.frontmatter.path,
        component: postTemplate,
        context: {},
      })
    })
  })
}

そしてcreatePageAPIのcomponentオプションでデータの挿入先として設定したテンプレートには以下のように記述します。

src/templates/post.js
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/Layout"

export default function BlogTemplate({
  data,
}) {
  const { markdownRemark } = data
  const { frontmatter, html } = markdownRemark

  return (
    <Layout>
      <SEO title={frontmatter.title} description={frontmatter.description} />
      <div>
        <article>
          <h1>{frontmatter.title}</h1>
          <ul>
            <li>
              <time>{frontmatter.date}</time>
            </li>
            <li>
              <time>{frontmatter.modified}</time>
            </li>
          </ul>
          <div dangerouslySetInnerHTML={{ __html: html }} />
        </article>
      </div>
    </Layout>
  )
}

export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "公開日:YYYY年MM月DD日")
        modified(formatString: "更新日:YYYY年MM月DD日")
        path
        title
        tags
        description
      }
    }
  }
`

このようにNext.jsやNuxt.jsと比較すると、自分で生成の処理を書かなければならない点がやや面倒でした。その点を解決するのが今回搭載されたFile System Route APIです。

File System Route APIのページ生成方法

File System Route APIのページ生成方法はgatsby-node.jsに自前で書いていたページ生成の処理が不要になるので削除します。次にテンプレートとなるファイルのファイル名を{Schema.Filed}の形で記述し、取得したいコンテンツを指定します。

その上でこれまでtemplates配下に設置していたテンプレートファイルをpages配下に移動することで実現可能となります。マークダウンからコンテンツを取得している私の環境の場合、ファイル名は{MarkdownRemark.frontmatter__path}.jsとなります。

src/pages/{MarkdownRemark.frontmatter__path}.js
import React from "react"
import { graphql } from "gatsby"
import styles from "./post.module.css"

const BlogPostTemplate = ({ params, data }) => {
  return (
    <article className={styles.post}>
      <h1 className={styles.postTitle}>{data.markdownRemark.frontmatter.title}</h1>
      <ul className={styles.postDateList}>
        <li className={styles.postDateListItem}>
          <time className={styles.postDate}>{data.markdownRemark.frontmatter.date}</time>
        </li>
        <li className={styles.postDateListItem}>
          <time className={styles.postDate}>{data.markdownRemark.frontmatter.modified}</time>
        </li>
      </ul>
      <div dangerouslySetInnerHTML={{ __html: data.markdownRemark.html }} />
    </article>
  )
}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug($id: String!) {
    markdownRemark(id: { eq: $id }) {
      id
      html
      frontmatter {
        title
        description
        date(formatString: "公開日:YYYY年MM月DD日")
        modified(formatString: "更新日:YYYY年MM月DD日")
        tags
      }
    }
  }
`

これだけでOKです。

gatsby-plugin-image

gatsby-plugin-imagegatsby-imageに代わる画像処理を行う新しいプラグインです。画像の取り回しの難しさはGatsbyの弱点の一つでしたが、その点を解消しているプラグインになっていると思いました。

gatsby-plugin-imageには静的画像用のStaticImageと動的画像用のGatsbyImageの2つのコンポーネントがあります。

StaticImage

静的画像を表示させたい場合に使用するコンポーネントです。静的画像というとややこしいですが、レンダリング時に毎回同じ画像を表示させたい場合はStaticImageを使用するというイメージで良さそうです。

従来のgatsby-imageだと例えsrcディレクトリに保存していた画像でもGraphQLを介さないと使用できませんでした。しかしStaticImageを使えばGraphQLを介さずに相対パスで読み込む形で画像の使用が可能となります。プレーンなHTMLの<img />と同じような使用感ですね。

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

export const Sample = () => (
  <StaticImage width={100} height={100} src="./images/sample.png" alt="sample" />
)

GatsbyImage

GatsbyImageは従来のgatsby-imageの似た仕様でGraphQLを介して画像を表示させます。StaticImageに対してGatsbyImageは動的画像を表示させる際に使用します。

また大きな変更点としてgatsby-imageの時には下記のようにfixedfluidクエリに複数のフラグメントが用意されており、やや面倒でした。

import { graphql } from "gatsby"
export const query = graphql`
  {
    file(relativePath: { eq: "images/example.jpg" }) {
      childImageSharp {
        fixed {
          ...GatsbyImageSharpFixed
        }
      }
    }
  }
`

この点、gatsby-plugin-imageではgatsbyImageDataという一つのフラグメントに集約され、ここにリゾルバーを渡す形に変更となり、よりシンプルな記述となりました。

import { graphql } from "gatsby"
export const query = graphql`
  {
    file(relativePath: { eq: "images/example.jpg" }) {
      childImageSharp {
        gatsbyImageData(layout: FIXED)
      }
    }
  }
`

gatsby-imageからgatsby-plugin-imageへの移行は公式のドキュメントに目を通すことをおすすめします。

Migrating from gatsby-image to gatsby-plugin-image

おわりに

今回のアップデートでGastbyを使用していて点が解消され、さらに使いやすくなった印象です。特に画像が扱いやすくなった点が個人的に大きいな変更でしたね。