こんにちはかみむらです。最近、GatsbyJSの注目度が高くなってると感じます。私はまだキャッチアップできていませんでしたが、色々学習リソースを調べてみたところ「The Great Gatsby Bootcamp」を見つけました。
The Great Gatsby Bootcampとは?
これは、YouTubeで学べるGatsbyJSのチュートリアルです。約4時間30分のコンテンツになります。
非常に質の高いチュートリアル動画ですが、全編英語です。しかし、英語が聞き取れない方でも動画を見ながら写経すれば大丈夫です。実際に、私は英語が全くわかりません笑
コンテンツ内容
動画の概要をざっと説明すると、GatsbyJSのプロジェクトの作成から、マークダウンの取得、ヘッドレスCMSのContentfulのAPI連携までをサポートしていて、簡易的なブログを作ることができます。例えば、今回完成するブログはこんな感じです。
ブログのリストページ
ブログの個別ページ
そして、動画の概要は下記の通りです。
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
試しにpagesのindex.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
9~15 ブログページの作成
マークダウンからデータを取得できるようにするためにgatsby-source-filesystemとgatsby-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
これでブログリストと、個別ページが完成します。
イメージの追加
ブログの個別ページに画像を追加するためのプラグインをインストールします。
$ 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)
アプリケーションを立ち上げて、個別ページを見てください。画像が最適化された状態で表示されます。
16~18 ContentfulのAPIと連携
次に、ContentfulのAPIと連携するために、コンテンツスペースとコンテンツモデルを作成します。
ブログのコンテンツモデルは下記のように作成します。
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で設定したsiteMetadataのtitleを取得しています。
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とアクセストークンを入力してください。
最後に
今回はGatsbyJSの基礎が学べる「The Great Gatsby Bootcamp」を紹介しました。私は今までGastbyJSのGraphQLに苦手意識をもち敬遠してましたが、この動画がきっかけでキャッチアップできました。
そのほかにもReactの基礎が学べるThe Complete React Web Developer Course
モダンなJavaScriptが学べる
などがあります。ぜひほかのコンテンツもみてください。