GatsbyJSをYouTubeで学べる「The Great Gatsby Bootcamp」をやってみたら最高だった

こんにちはかみむらです。最近、GatsbyJSの注目度が高くなってると感じます。私はまだキャッチアップできていませんでしたが、色々学習リソースを調べてみたところ「The Great Gatsby Bootcamp」を見つけました。

The Great Gatsby Bootcampとは?

www.youtube.com

これは、YouTubeで学べるGatsbyJSのチュートリアルです。約4時間30分のコンテンツになります。

非常に質の高いチュートリアル動画ですが、全編英語です。しかし、英語が聞き取れない方でも動画を見ながら写経すれば大丈夫です。実際に、私は英語が全くわかりません笑

コンテンツ内容

動画の概要をざっと説明すると、GatsbyJSのプロジェクトの作成から、マークダウンの取得、ヘッドレスCMSのContentfulのAPI連携までをサポートしていて、簡易的なブログを作ることができます。例えば、今回完成するブログはこんな感じです。

ブログのリストページ

f:id:kamimura-dev:20200227223033p:plain

ブログの個別ページ

f:id:kamimura-dev:20200227223246p:plain

そして、動画の概要は下記の通りです。

1. Creating a Gatsby Site(0.38)
2. Working with Gatsby Pages(14.50)
3. Linking Between Pages with Gatsby(27:00)
4. Creating Shared Page Components(35:56)
5. Creating Gatsby Page Layouts(48:14)
6. Styling Gatsby Projects(56:13)
7. Styling Gatsby with CSS Modules(1:06:49)
8. Gatsby Data with GraphQL(1:28:23)
9. GraphQL Playground(1:47:12)
10. Sourcing Content from the File System(1:51:32)
11. Working with Markdown Posts(2:03:37)
12. Generating Slugs for Posts(2:19:00)
13. Dynamically Generating Pages(2:35:14)
14. Rendering Post Data in Blog Template(2:52:08)
15. Adding Images to Posts(3:03:28)
16. Getting Started with Contentful(3:21:19)
17. Rendering Contentful Posts(3:38:29)
18. Dynamic Pages from Contentful(3:49:24)
19. 404 Pages and React Helmet(4:10:18)
20. Deploying Your Gatsby Site(4:25:38)

少し長く感じますが要点をピックアップしていきます。

1~7 GatsbyJSプロジェクトの作成からレイアウトの構成

まずは、GatsbyJSの公式CLIをグローバル環境にインストールします。

$ npm install -g gatsby-cli

そして、プロジェクトの雛形を作成するためのnewコマンドを打ちます。コマンドの引数にURLを指定すると、スターターテンプレートを生成してくれます。

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

試しにpagesindex.jsの内容を書き換えてみましょう。

pages/index.js

import React from "react"

const IndexPage = () => {
  return (
    <div>
      <h1>Hello.</h1>
      <h2>I'm kamimura, a full-stack developer living in beatiful Phiadelphia</h2>
    </div>
  )
}
export default IndexPage

無事にアプリケーションが起動するか、開発サーバーを立ち上げてみましょう。

$ npm run develop

レイアウトの作成

ヘッダーやフッターなどの共通コンポーネントは、layoutコンポーネントにインポートして共通化します。Reactのchildrenを使います。

components/layout.js

import React from "react"

import Header from './header';
import Footer from "./footer"

const Layout = props => {
  return (
    <div>
      <Header />
      {props.children}
      <Footer />
    </div>
  )
}

export default Layout

そして、ページ毎にlayoutコンポーネントをインポートします。

import React from "react"
import Layout from "../components/layout"

const IndexPage = () => {
  return (
    <Layout>
      <h1>Hello.</h1>
      <h2>I'm hiro, a full-stack developer living in beatiful Phiadelphia</h2>
    </Layout>
  )
}
export default IndexPage

Sassの導入

GatsbyJSでSassを導入するのは簡単で、対応するプラグインをインストールします。

$ npm install --save gatsby-plugin-sass node-sass

そして、gatsby-config.jsにSassのプラグインを追加します。

gatsby-config.js

module.exports = {
  plugins: ["gatsby-plugin-sass"],
}

これでSassを導入することができました。動画の通りにスタイルを当てるとこんな感じになります。

f:id:kamimura-dev:20200227215312p:plain

9~15 ブログページの作成

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

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

そして、インストールしたプラグインの設定を追記します。

gatsby-config.js

module.exports = {
  siteMetadata: {
    title: "Full-Stack Bootcamp",
    author: "kamimura",
  },
  plugins: [
    "gatsby-plugin-sass",
    {
      resolve: "gatsby-source-filesystem",
      options: {
        name: "src",
        path: `${__dirname}/src/`,
      },
    },
    "gatsby-transformer-remark",
  ],
}

試しに、マークダウンファイルを作成してみましょう。postsフォルダreact.mdを作成してみます。

posts/react.md

---
title: "react"
date: "2020-02-27"
---

I just launched a new bootcamp!

## Topics Covered

test

先程インストールした、gatsby-transformer-remarkによってマークダウンのデータをパースしてくれます。これで、GraphQL経由でマークダウンデータを取得できるようになります。これがブログリストのページです。

pages/blog.js

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

import Layout from "../components/layout"

const BlogPage = () => {
  const data = useStaticQuery(graphql`
    query {
      allMarkdownRemark {
        edges {
          node {
            frontmatter {
              title
              date
            }
            fields {
              slug
            }
          }
        }
      }
    }
  `)

  return (
    <Layout>
      <h1>Blog</h1>
      {data.allMarkdownRemark.edges.map(edge => {
        return (
          <li>
            <Link to={`/blog/${edge.node.fields.slug}`}>
              <h2>{edge.node.frontmatter.title}</h2>
              <p>{edge.node.frontmatter.date}</p>
            </Link>
          </li>
        )
      })}
    </Layout>
  )
}

export default BlogPage

個別ページのURLはslugにしたいので、gatsby-node.jsに設定します。また、ここではblogTemplateを指定しているところに注目してください。これで、templateフォルダblog.jsに個別ページの内容を表示させることができます。

gatsby-node.js

const path = require("path")

module.exports.onCreateNode = ({ node, actions }) => {
  const { createNodeField } = actions

  if (node.internal.type === "MarkdownRemark") {
    const slug = path.basename(node.fileAbsolutePath, ".md")

    createNodeField({
      node,
      name: "slug",
      value: slug,
    })
  }
}

module.exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
  const blogTemplate = path.resolve("./src/templates/blog.js")
  const res = await graphql(`
    query {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
    }
  `)

  res.data.allMarkdownRemark.edges.forEach(edge => {
    createPage({
      component: blogTemplate,
      path: `/blog/${edge.node.fields.slug}`,
      context: {
        slug: edge.node.fields.slug,
      },
    })
  })
}

こちらが個別ページの内容です。ブログリストのページと同じように、GraphQLでデータを取得することができます。

templates/blog.js

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

import Layout from "../components/layout"

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

const Blog = props => {
  return (
    <Layout>
      <h1>{props.data.markdownRemark.frontmatter.title}</h1>
      <p>{props.data.markdownRemark.frontmatter.date}</p>
      <div
        dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }}
      ></div>
    </Layout>
  )
}

export default Blog

これでブログリストと、個別ページが完成します。

f:id:kamimura-dev:20200227220321p:plain

イメージの追加

ブログの個別ページに画像を追加するためのプラグインをインストールします。

$ npm install --save gatsby-plugin-sharp gatsby-remak-images gatsby-remark-relative-images

そして、イメージを表示するための設定を追加します。

gatsby-config.js

module.exports = {
    (中略)
    "gatsby-plugin-sharp",
    {
      resolve: "gatsby-transformer-remark",
      options: {
        plugins: [
          "gatsby-remark-relative-images",
          {
            resolve: "gatsby-remark-images",

            options: {
              maxWidth: 750,
              linkImagesToOriginal: false,
            },
          },
        ],
      },
}

これでブログの個別ページに画像を最適化した状態で表示することができます。例えば下記のように、マークダウンファイルに画像を追加します。

posts/react.md

![Grass](./grass.png)

アプリケーションを立ち上げて、個別ページを見てください。画像が最適化された状態で表示されます。

f:id:kamimura-dev:20200227220404p:plain

16~18 ContentfulのAPIと連携

次に、ContentfulのAPIと連携するために、コンテンツスペースとコンテンツモデルを作成します。

www.contentful.com

f:id:kamimura-dev:20200227173718p:plain

ブログのコンテンツモデルは下記のように作成します。

f:id:kamimura-dev:20200227173730p:plain

ContentfulからGatsbyJSで、APIを取るための実装をしていきます。対応したプラグインをインストールします。

$ npm install --save gatsby-source-contentful @contentful/rich-text-react-renderer

そして、プラグインの設定を追記します。

gatsby-config.js

module.exports = {
  (中略)
  {
      resolve: 'gatsby-source-contentful',
      options: {
          spaceId: process.env.CONTENTFUL_SPACE_ID,
          accessToken: process.env.CONTENTFUL_ACCESS_TOKEN
      }
  },
}

Contentfulで取得したIDとアクセストークンは.env.developmentで保護しましょう。

.env.development

CONTENTFUL_SPACE_ID=xxxxxxxxx
CONTENTFUL_ACCESS_TOKEN=xxxxxxx

gatsby-node.jsもContentfulのAPIを取得するための設定に書き換える必要があります。

gatsby-node.js

const path = require("path")

module.exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
  const blogTemplate = path.resolve("./src/templates/blog.js")
  const res = await graphql(`
    query {
      allContentfulBlogPost {
        edges {
          node {
            slug
          }
        }
      }
    }
  `)

  res.data.allContentfulBlogPost.edges.forEach(edge => {
    createPage({
      component: blogTemplate,
      path: `/blog/${edge.node.slug}`,
      context: {
        slug: edge.node.slug,
      },
    })
  })
}

そして、こちらがContentfulのAPIを取得する個別ページです。

templates/blog.js

import React from "react"
import { graphql } from "gatsby"
import { documentToReactComponents } from "@contentful/rich-text-react-renderer"

import Layout from "../components/layout"
import Head from "../components/head"

export const query = graphql`
  query($slug: String!) {
    contentfulBlogPost(slug: { eq: $slug }) {
      title
      publishedDate(formatString: "MMMM Do, YYYY")
      body {
        json
      }
    }
  }
`

const Blog = props => {
  const options = {
    renderNode: {
      "embedded-asset-block": (node) => {
        const alt = node.data.target.fields.title['en-US']
        const url = node.data.target.fields.file['en-US'].url
        return <img alt={alt} src={url} />
      }
    }
  }

  return (
    <Layout>
      <Head title={props.data.contentfulBlogPost.title}/>
      <h1>{props.data.contentfulBlogPost.title}</h1>
      <p>{props.data.contentfulBlogPost.publishedDate}</p>
      {documentToReactComponents(props.data.contentfulBlogPost.body.json, options)}
    </Layout>
  )
}

export default Blog

ContentfulのAPIをリストで表示するページです。

pages/blog.js

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

import Layout from "../components/layout"
import blogStyles from "./blog.module.scss"
import Head from "../components/head"

const BlogPage = () => {
  const data = useStaticQuery(graphql`
    query {
      allContentfulBlogPost(sort: { fields: publishedDate, order: DESC }) {
        edges {
          node {
            title
            slug
            publishedDate(formatString: "MMMM Do, YYYY")
          }
        }
      }
    }
  `)

  return (
    <Layout>
      <Head title="Blog" />
      <h1>Blog</h1>
      <ol className={blogStyles.posts}>
        {data.allContentfulBlogPost.edges.map(edge => {
          return (
            <li className={blogStyles.post}>
              <Link to={`/blog/${edge.node.slug}`}>
                <h2>{edge.node.title}</h2>
                <p>{edge.node.publishedDate}</p>
              </Link>
            </li>
          )
        })}
      </ol>
    </Layout>
  )
}

export default BlogPage

19 メタ情報の実装と404ページの追加

ここからはより実用的なブログにしていくために、メタ情報の実装と、404ページの追加をします。メタ情報を設定するためのプラグインとライブラリをインストールします。

$ npm install --save react-helmet gatsby-plugin-react-helmet

プラグインの設定をします。

gatsby-config.js

plugins: [
  'gatsby-plugin-react-helmet'
]


ここではgatsby-config.jsで設定したsiteMetadatatitleを取得しています。

components/head.js

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

const Head = ({ title }) => {
  const data = useStaticQuery(graphql`
    query {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)

  return <Helmet title={`${title} | ${data.site.siteMetadata.title}`} />
}

export default Head

404ページの作成

GatsbyJSで404ページを追加するには、pagesフォルダ内に404.jsを作成します。これで、サイト内にページが存在しない場合に404ページを表示してくれます。

pages/404.js

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

import Layout from '../components/layout'
import Head from "../components/head"

const NotFound = () => {
    return (
        <Layout>
            <Head title="404"/>
            <h1>Page not found</h1>
            <p><Link to="/">Head home</Link></p>
        </Layout>
    )
}

export default NotFound

20 Netlifyへデプロイ

最後にローカルで作成したGatsbyJSのWebサイトをNetlifyにデプロイしていきます。プロジェクトをGitHubにプッシュしてください。

そして、GitHubとNetlifyを連携して、デプロイ設定をしていきます。Advaced build settingsに先ほど確認したContentfulのIDとアクセストークンを入力してください。

www.netlify.com

f:id:kamimura-dev:20200227101641p:plain

最後に

今回はGatsbyJSの基礎が学べる「The Great Gatsby Bootcamp」を紹介しました。私は今までGastbyJSのGraphQLに苦手意識をもち敬遠してましたが、この動画がきっかけでキャッチアップできました。

そのほかにもReactの基礎が学べるThe Complete React Web Developer Course

www.youtube.com

モダンなJavaScriptが学べる

www.youtube.com

などがあります。ぜひほかのコンテンツもみてください。