Gatsbyチュートリアル
静的サイトジェネレータとして有名なGatsby。
触ったことが無かったが、ちょうど使用する機会があったため、公式チュートリアルを一通りやってみる。
その雑多なまとめ。
今回触れていること
公式チュートリアルはこちら www.gatsbyjs.com
とても親切に出来ていて助かる。
Gatsbyとは
この記事が分かりやすい。 qiita.com
ここも見とくと具体的に何が利点なのか分かる。 qiita.com
特徴としては以下が挙げられる。
- Reactで作られた静的サイトジェネレーター
- 静的サイトジェネレーターだが、axiosなどを使った、非同期のdata fetchも可能。
- GraphQLと相性良い
- Gatsbyプラグインが多数あり便利(TypeScript化,PWA対応...)
- 爆速
- サーバーを気にしないで済む
Reactを触ったことがある人ならすぐ使える。
学習コストがあまり高くないのは利点。
GraphQLは完全に把握するのは少し難易度高そうな感じ。
仕組みを大まかに把握して所で実際に触ってみる。
0. Set Up Your Development Environment
環境構築はチュートリアルに沿っていけば問題ない。
# gatsbyサイト新規作成 gatsby new [SITE_DIRECTORY_NAME] [URL_OF_STARTER_GITHUB_REPO] # 開発サーバー起動(http://localhost:8000/ で確認) gatsby develop
1. Get to Know Gatsby Building Blocks(Surgeを使用したデプロイ)
JSXの説明、Reactの説明がされているがこの部分はさっと読み流し。
「src/pages/」に設置するとページとして認識される。
試しに「src/pages/about.js」を作成し、「http://localhost:8000/about」で確認してみるとページが確認できる。
再利用できるコンポーネントはサブコンポーネントとして「src/components」に作る。
「src/components/header.js」を作成し、about.jsで読み込んでみるとheader.jsが/aboutに表示されていることが確認できる。
Gatsbyコンポーネント(Link)の使用は以下の様に行う。
import React from "react"
import { Link } from "gatsby"
import Header from "../components/header"
export default function Home() {
return (
<div style={{ color: `purple` }}>
<Link to="/contact/">Contact</Link>
<Header headerText="Hello Gatsby!" />
<p>What a world.</p>
<img src="https://source.unsplash.com/random/400x200" alt="" />
</div>
)
}
Gatsbyサイトで処理されないページへの外部リンクについては、通常のaタグで行うとのこと。
Surgeを使用したデプロイが紹介されていたので試す。
npm install --global surge # surgeアカウントがない場合は画面に従って作成 surge login # ビルドするとpublicフォルダに生成される gatsby build # surgeデプロイ surge public/ # domainに記載されているURLを開くとページが確認できる
surgeについてはこちら surge.sh
コマンド解説 dotstud.io
2. Introduction to Styling in Gatsby
グローバルスタイルを試す。
gatsby-browser.jsをルートディレクトリに作成し、任意の場所に作成したCSSファイルを読み込むと、全体のページにスタイルが適用されているのが確認できる。
gatsby-browser.js
import "./src/styles/global.css"
CSSモジュールを試す。
以下のディレクトリとファイルを作成。
src/components/container.js
import React from "react"
import containerStyles from "./container.module.css"
export default function Container({ children }) {
return <div className={containerStyles.container}>{children}</div>
}
src/components/container.module.css
.container {
margin: 3rem auto;
max-width: 600px;
}
src/pages/about-css-modules.js
import React from "react"
import Container from "../components/container"
export default function About() {
return (
<Container>
<h1>About CSS Modules</h1>
<p>CSS Modules are cool</p>
</Container>
)
}
/about-css-modulesを確認するとCSSが適用されていることが確認できる。
コンポーネントを作成し、そのコンポーネントにスタイルを設定する場合は以下の様にする。
src/pages/about-css-modules.js
import React from "react"
import styles from "./about-css-modules.module.css"
import Container from "../components/container"
const User = props => (
<div className={styles.user}>
<img src={props.avatar} className={styles.avatar} alt="" />
<div className={styles.description}>
<h2 className={styles.username}>{props.username}</h2>
<p className={styles.excerpt}>{props.excerpt}</p>
</div>
</div>
)
export default function About() {
return (
<Container>
<h1>About CSS Modules</h1>
<p>CSS Modules are cool</p>
<User
username="Jane Doe"
avatar="https://s3.amazonaws.com/uifaces/faces/twitter/adellecharles/128.jpg"
excerpt="I'm Jane Doe. Lorem ipsum dolor sit amet, consectetur adipisicing elit."
/>
<User
username="Bob Smith"
avatar="https://s3.amazonaws.com/uifaces/faces/twitter/vladarbatov/128.jpg"
excerpt="I'm Bob Smith, a vertically aligned type of guy. Lorem ipsum dolor sit amet, consectetur adipisicing elit."
/>
</Container>
)
}
3. Creating Nested Layout Components
Typography.jsの使用を試す。
npm install gatsby-plugin-typography react-typography typography typography-theme-fairy-gates
ルートディレクトリにgatsby-config.jsを作成。
gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-typography`,
options: {
pathToConfigModule: `src/utils/typography`,
},
},
],
}
gatsby-config.jsはプラグインやその他のサイト構成を追加するファイルとのこと。
詳細はここから。
www.gatsbyjs.com
Typography.jsの設定ファイルを作成
src/utils/typography.js
import Typography from "typography"
import fairyGateTheme from "typography-theme-fairy-gates"
const typography = new Typography(fairyGateTheme)
export const { scale, rhythm, options } = typography
export default typography
開発サーバーを再起動して、Chrome開発者ツールでheadタグ内を見てみると、
styleタグ内にTypography.jsのスタイルが記載されていることが確認できる。
また、ページ内のフォントが変更されていることが確認できる。
レイアウトコンポーネントについて。
以下の様に枠を作成して、呼び出すだけで統一したレイアウトにすることが出来る。
src/components/layout.js
import React from "react"
export default function Layout({ children }) {
return (
<div style={{ margin: `3rem auto`, maxWidth: 650, padding: `0 1rem` }}>
{children}
</div>
)
}
src/pages/index.js
import React from "react"
import Layout from "../components/layout"
export default function Home() {
return (
<Layout>
<h1>Hi! I'm building a fake Gatsby site as part of a tutorial!</h1>
<p>
What do I like to do? Lots of course but definitely enjoy building
websites.
</p>
</Layout>
);
}
layout.jsにナビゲーションリンクなど付けると更に便利だよ、というような内容だった。
4. Data in Gatsby(GraphQL)
GraphQLの公式サイトにもチュートリアルがある。 www.howtographql.com
まず、サイトのタイトルがバラバラなので1つの場所にまとめましょうとのこと。
gatsby-config.jsのsiteMetadata>titleに任意のタイトル名を記載。
gatsby-config.js
module.exports = {
siteMetadata: {
title: `Title from siteMetadata`,
},
plugins: [
`gatsby-plugin-emotion`,
{
resolve: `gatsby-plugin-typography`,
options: {
pathToConfigModule: `src/utils/typography`,
},
},
],
}
以下の様にするとgatsby-config.jsで指定したtitleを使用できる。
開発サーバーを再起動すると確認できる。
gatsbyからgraphqlを読み込んで、const queryを作成している。
ページコンポーネントでのみ使用可能とのこと。
src/pages/about.js
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
export default function About({ data }) {
return (
<Layout>
<h1>About {data.site.siteMetadata.title}</h1>
<p>
We're the only site running on your computer dedicated to showing the
best photos and videos of pandas eating lots of food.
</p>
</Layout>
)
}
export const query = graphql`
query {
site {
siteMetadata {
title
}
}
}
`
StaticQueryの使用を試す。
StaticQueryはGatsbyのバージョン2で導入された新しいAPIで、ページ以外のコンポーネント(layout.jsコンポーネントなど)がGraphQLクエリを介してデータを取得できる、とのこと。
useStaticQueryを使用する。
src/components/layout.js
import React from "react"
import { css } from "@emotion/core"
import { useStaticQuery, Link, graphql } from "gatsby"
import { rhythm } from "../utils/typography"
export default function Layout({ children }) {
const data = useStaticQuery(
graphql`
query {
site {
siteMetadata {
title
}
}
}
`
)
return (
<div
css={css`
margin: 0 auto;
max-width: 700px;
padding: ${rhythm(2)};
padding-top: ${rhythm(1.5)};
`}
>
<Link to={`/`}>
<h3
css={css`
margin-bottom: ${rhythm(2)};
display: inline-block;
font-style: normal;
`}
>
{data.site.siteMetadata.title}
</h3>
</Link>
<Link
to={`/about/`}
css={css`
float: right;
`}
>
About
</Link>
{children}
</div>
)
}
gatsby-config.jsのタイトルを変えてみるとすぐに反映することが確認できる。
5. Source Plugins
GraphQLとソースプラグインを使用してGatsbyサイトにデータを取り込む方法について試す。
名前がややこしいがGraphiQLというGraphQL統合開発環境があるらしい。 http://localhost:8000/___graphqlでアクセスできる。
使いたいデータ構造を予め作成できる感じか。
Gatsbyサイトのデータは、API、データベース、CMS、ローカルファイルなど、どこからでも取得できるとのこと。
例としてgatsby-source-filesystemを使用している。
npm install gatsby-source-filesystem
gatsby-config.jsにgatsby-source-filesystemを追加。
gatsby-config.js
module.exports = {
siteMetadata: {
title: `Pandas Eating Lots`,
},
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `src`,
path: `${__dirname}/src/`,
},
},
`gatsby-plugin-emotion`,
{
resolve: `gatsby-plugin-typography`,
options: {
pathToConfigModule: `src/utils/typography`,
},
},
],
}
開発サーバーを再起動し、GraphiQLで確認すると、allFile、fileが追加されている。
GraphiQLでクエリにフィールドを追加して、実行すると結果が確認できる。
因みにExplolerボタンでコピペ用のコードが表示される。
試しに以下の様にして確認してみる。
(/pageで作成したファイル一覧が表示される)
src/pages/my-files.js
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
export default function MyFiles({ data }) {
console.log(data)
return (
<Layout>
<div>
<h1>My Site's Files</h1>
<table>
<thead>
<tr>
<th>relativePath</th>
<th>prettySize</th>
<th>extension</th>
<th>birthTime</th>
</tr>
</thead>
<tbody>
{data.allFile.edges.map(({ node }, index) => (
<tr key={index}>
<td>{node.relativePath}</td>
<td>{node.prettySize}</td>
<td>{node.extension}</td>
<td>{node.birthTime}</td>
</tr>
))}
</tbody>
</table>
</div>
</Layout>
)
}
export const query = graphql`
query {
allFile {
edges {
node {
relativePath
prettySize
extension
birthTime(fromNow: true)
}
}
}
}
`
6. Transformer plugins
ファイル内のデータをクエリすることを可能にするためにTransformer pluginsがあるとのこと。
例としてマークダウンファイルをHTMLに変換するgatsby-transformer-remarkを使用している。
ここでやっていることは、マークダウンファイルを読み込んでトップページにリストとして表示、という感じ。
マークダウンファイル作成
src/pages/sweet-pandas-eating-sweets.md
--- title: "Sweet Pandas Eating Sweets" date: "2017-08-10" --- Pandas are really sweet. Here's a video of a panda eating sweets. <iframe width="560" height="315" src="https://www.youtube.com/embed/4n0xNbfJLR8" frameborder="0" allowfullscreen></iframe>
npm install gatsby-transformer-remark
gatsby-config.js修正
gatsby-config.js
module.exports = {
siteMetadata: {
title: `Pandas Eating Lots`,
},
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `src`,
path: `${__dirname}/src/`,
},
},
`gatsby-transformer-remark`,
`gatsby-plugin-emotion`,
{
resolve: `gatsby-plugin-typography`,
options: {
pathToConfigModule: `src/utils/typography`,
},
},
],
}
開発サーバーを再起動すると、allMarkdownRemarkが追加されているので、マークダウンファイルに関するフィールドを使用することができる。
index.jsを修正して試す。
src/pages/index.js
import React from "react"
import { graphql } from "gatsby"
import { css } from "@emotion/core"
import { rhythm } from "../utils/typography"
import Layout from "../components/layout"
export default function Home({ data }) {
console.log(data)
return (
<Layout>
<div>
<h1
css={css`
display: inline-block;
border-bottom: 1px solid;
`}
>
Amazing Pandas Eating Things
</h1>
<h4>{data.allMarkdownRemark.totalCount} Posts</h4>
{data.allMarkdownRemark.edges.map(({ node }) => (
<div key={node.id}>
<h3
css={css`
margin-bottom: ${rhythm(1 / 4)};
`}
>
{node.frontmatter.title}{" "}
<span
css={css`
color: #bbb;
`}
>
— {node.frontmatter.date}
</span>
</h3>
<p>{node.excerpt}</p>
</div>
))}
</div>
</Layout>
)
}
export const query = graphql`
query {
allMarkdownRemark {
totalCount
edges {
node {
id
frontmatter {
title
date(formatString: "DD MMMM, YYYY")
}
excerpt
}
}
}
}
`
新たなマークダウンファイルを作成すると、/indexの表示が変わることが確認できる。
またソートも可能で、GraphiQLでallMarkdownRemarkのソートフィールドを追加すれば、ソートが可能になる。
ここで便利さを改めて実感した。
7. Programmatically create pages from data
今度はマークダウンファイルごとのページを作成する。
そのためにはslugの作成が必要とのこと(https://www.gatsbyjs.com/tutorial/part-seven/の/tutorial/part-sevenの部分をslugと言う)。
Gatsby APIの内、2つのAPI(onCreateNode,createPages)を使用してマークダウンファイルからページを作成する。
Gatsby APIを使用するにはgatsby-node.jsを作成し、APIの名前を付けて関数をエクスポートする。
gatsby-node.js
exports.onCreateNode = ({ node }) => {
console.log(`Node created of type "${node.internal.type}"`)
}
開発サーバーを再起動すると、ターミナルにコンソールログが表示される。
進んでいくと、以下の様なファイル名を取得する処理の話になる。
MarkdownRemarkノードではファイル名が取得できないため、
getNode(node.parent)で親ノードを指定し、そこからrelativePathを取得しているとのこと。
gatsby-node.js
exports.onCreateNode = ({ node, getNode }) => {
if (node.internal.type === `MarkdownRemark`) {
const fileNode = getNode(node.parent)
console.log(`\n`, fileNode.relativePath)
}
}
ただ、gatsby-source-filesystemで既に機能としてあるため以下の様な書き方で、slugの作成まで行うことが可能。
gatsby-node.js
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode }) => {
if (node.internal.type === `MarkdownRemark`) {
console.log(createFilePath({ node, getNode, basePath: `pages` }))
}
}
次に、nodeの作成(API→onCreateNode)。
gatsby-node.js
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
GraphiQLで確認すると、新たなslugが追加されている。
次に、ページ作成(API→createPages)。
gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`)
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
// Data passed to context is available
// in page queries as GraphQL variables.
slug: node.fields.slug,
},
})
})
}
テンプレートを通してページとなるため、専用のファイルを作成。
src/templates/blog-post.js
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
export default function BlogPost({ data }) {
const post = data.markdownRemark
return (
<Layout>
<div>
<h1>{post.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
</Layout>
)
}
export const query = graphql`
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
}
}
}
`
開発サーバーを再起動し、マークダウンファイルの名前でアクセスするか、
適当にhttp://localhost:8000/sdfと存在しないパスを指定してから、対象のページへリンクすると確認できる。
最後にトップページにリンクを設定。
src/pages/index.js
import React from "react"
import { css } from "@emotion/core"
import { Link, graphql } from "gatsby"
import { rhythm } from "../utils/typography"
import Layout from "../components/layout"
export default function Home({ data }) {
return (
<Layout>
<div>
<h1
css={css`
display: inline-block;
border-bottom: 1px solid;
`}
>
Amazing Pandas Eating Things
</h1>
<h4>{data.allMarkdownRemark.totalCount} Posts</h4>
{data.allMarkdownRemark.edges.map(({ node }) => (
<div key={node.id}>
<Link
to={node.fields.slug}
css={css`
text-decoration: none;
color: inherit;
`}
>
<h3
css={css`
margin-bottom: ${rhythm(1 / 4)};
`}
>
{node.frontmatter.title}{" "}
<span
css={css`
color: #555;
`}
>
— {node.frontmatter.date}
</span>
</h3>
<p>{node.excerpt}</p>
</Link>
</div>
))}
</div>
</Layout>
)
}
export const query = graphql`
query {
allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
totalCount
edges {
node {
id
frontmatter {
title
date(formatString: "DD MMMM, YYYY")
}
fields {
slug
}
excerpt
}
}
}
}
`
8. Preparing a Site to Go Live
Lighthouseでサイト診断。
# ビルド gatsby build # ローカルで本番サイトを確認するためのコマンド gatsby serve
こんな感じで診断結果が表示される。

ここでチュートリアルは大体完了。
9.GAEへデプロイ
GCPは完全に初心者だったので、まず環境構築からだったが、デプロイ完了までそこまで時間はかからなかった(1,2時間くらい?)。
以下の記事を元に、まずデプロイするまでざっくりやってみる。
公式サイトとこちらの記事を参考。 cloud.google.com shotat.hateblo.jp
gcloudコマンドのインストール等も公式とこちらの記事を参考にした。 qiita.com
初期設定等が完了したら、デプロイするアプリのルートディレクトリにapp.yamlを作成。
runtime: php55 api_version: 1 handlers: # file with extensions (e.g. .html) - url: /(.*\..*) static_files: public/\1 upload: public/(.*) - url: /(.*)/ static_files: public/\1/index.html upload: public/(.*)/index.html - url: / static_files: public/index.html upload: public/index.html - url: /(.*) static_files: public/\1/index.html upload: public/(.*)/index.html skip_files: - node_modules/ - src/ - \.cache/ - package\.json - yarn\.lock
後はコマンドを打つだけ。
# デプロイ gcloud app deploy # アプリの表示 gcloud app browse
まとめ
次は簡単なwebアプリを作ってみる。
GraphQL、GCP(GAE)も今回初めて触ったので、まとめて記事にする。
Next.jsもあまり明るくないので、実際に触ってみてGatsbyと比較してみる。