Qiitaに書いたやつ

Gatsbyを最速で理解するためのチュートリアル

JavaScript静的サイトジェネレーターReactgatsby
2019年12月14日

Gatsbyを触ってみたのですがチュートリアルだけだと??となる部分がいくつもあり結局ドキュメントをたくさん読むハメになりました。 ドキュメントのリンクを整理してたらいい感じのチュートリアルができたので投稿しました(所々中途半端ですが・・・)

Install

npm install -g gatsby-cli
gatsby new [rootPath] [starter]  Create new Gatsby project.

starterを指定する。デフォルトではdefault starterが選択されます。

チュートリアルなのでhello-world一択。

gatsby new my-blog https://github.com/gatsbyjs/gatsby-starter-hello-world
gatusby deplop 

一応npm scriptにも書かれています。

npm run deplop 
 .json
 "scripts": {
    "develop": "gatsby develop"
  }

-pオプションで別のポートを指定することができるので複数のプロジェクトを立ち上げたいときに。

gatusby deplop -p 3000

CLIのドキュメントまで丁寧にあります。

デフォルトだとhttp://localhost:8000/ でページにhttp://localhost:8000/___graphql でGrqphiQLにアクセスできます。

入門

Page

ディレクトリの構成にルールがあってpages/が自動でページなります。 例えばpages/about.jsを作成すると/aboutで表示されます(ページが生成されてルートが設定される)

pages/index.js
import React from 'react'

export default <div> index page </div>
pages/about.js
import React from 'react'

export default <div> hello page </div>

その他はcomponents/に共通コンポネントを作成するなど普通のReactのアプリケーションのようにを開発できます。

Any React component defined in src/pages/*.js will automatically become a page.

using-page-components

またテンプレートファイルを作成して動的にページを生成することも可能です。creating-pages-automatically

GraphQL

gatsbyの便利な点はプロジェクト内のデータをGraphqlで取得できるところです。

例えばProjectの設定情報は以下のqueryで取得できます。

configファイルを以下のように変更します。

module.exports = {
  siteMetadata: {
    title: `Title from siteMetadata`,
  }
}
  query {
    site {
      siteMetadata {
        title
      }
    }
  }

デモ

このようにプロジェクトの情報をqueryで取得して、それを表示するReactのコンポネントを作成し、それを元にgatsbyがページを生成するという流れになります。

use-a-page-query

このGraphQLですがデータを追加する作業とデータを取得する作業の二つに分けて理解するのがいいと思います。


まずデータを追加する作業から見ていきます。 後述するプラグインを入れる事でプロジェクト内の情報を自由に取得できるようになります。

adding-data-to-graphql

プラグインからデータを追加する

データを追加する作業はドキュメントにplugin drivenと書かれていて、実際プラグインを入れるだけです。

sourcing-data

gatsby-source-filesystem

例えばgatsby-source-filesystemを使うことでソースからデータを取得できるようになります。

npm install --save gatsby-source-filesystem
gatsby-config.js.js
module.exports = {
  siteMetadata: {
    title: `Pandas Eating Lots`,
  },
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `src`,
        path: `${__dirname}/src/`,
      },
    }
  ]
}

graphiqlを開くとallFilefileが新たに追加され、以下のqueryでファイル情報を取得できるようになります。

  query {
    allFile {
      edges {
        node {
          relativePath
          prettySize
          extension
          birthTime(fromNow: true)
        }
      }
    }
  }

https://www.gatsbyjs.org/tutorial/part-five/

チュートリアルではプロジェクトのJSファイルを取得して表示しています。

gatsby-transformer-remark

https://www.gatsbyjs.org/docs/adding-markdown-pages/#transform-markdown-to-html-and-frontmatter-to-data-using-gatsby-transformer-remark

これはデータを追加するだけのプラグインではないので、この流れで書くのも微妙なのですが このプラグインがやってくれる一番わかりやすいのはmarkdownを変換してくれるところです。mdファイルの情報をhtmlに変換して取得できたり、frontmatterなどのメタ情報を取得できます。

allMarkdownRemarkというのが追加されます。

自分でデータを追加する

createNodeAPIを使って自分でデータを流すことができます。後述しますがプラグインはこのAPIを使ってデータを流しています。

手動でデータを入れてみる
ポケモンのデータを入れてみる
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
  const pokemons = [
    { name: "Pikachu", type: "electric" },
    { name: "Squirtle", type: "water" },
  ]
  pokemons.forEach(pokemon => {
    const node = {
      name: pokemon.name,
      type: pokemon.type,
      id: createNodeId(`Pokemon-${pokemon.name}`),
      internal: {
        type: "Pokemon",
        contentDigest: createContentDigest(pokemon),
      },
    }
    actions.createNode(node)
  })
}

以下のqueryでポケモンを取得できるようになります。

query MyPokemonQuery {
  allPokemon {
    nodes {
      name
      type
      id
    }
  }
}

外部から取得したデータを流すこともできる。

APIから取得したデータを流してみる
QiitaのAPIからデータを流してみる

この辺のAPIは詳しく確認してないので、少し修正しただけですが、

const fetch = require("node-fetch")

const fetchItems = () =>
  fetch("https://qiita.com/api/v2/items").then(res => res.json())

exports.sourceNodes = async ({
  actions,
  createNodeId,
  createContentDigest,
}) => {
  const items = await fetchItems()
  items.forEach(item => {
    const node = {
      id: item.id,
      title: item.title,
      user: {
        name: item.user.name,
      },
      internal: {
        type: "Item",
        contentDigest: createContentDigest(item),
      },
    }
    actions.createNode(node)
  })
}
query ItemQuery {
  allItem {
    nodes {
      title
      user {
        name
      }
    }
  }
}
{
  "data": {
    "allItem": {
      "nodes": [
        {
          "title": "blob & createObjectURL について ",
          "user": {
            "name": "Yu Watanabe"
          }
        },
      ]
   }
  }
}

QiitaのAPIからとってきた情報を流す事で、queryで取得できるようになりました。

先ほどのgatsby-source-filesystemもこのcreateNodeを使ってローカルのファイル一覧を取得して流し込んでいるのがわかると思います。

ファイルの情報を流すとかwordpressから記事を取得して流すといったことをやってくれるプラグインがあるので、我々は設定ファイルにパスだったりurlを入れるだけでqueryでデータを取得できるようになります。

pixabay-source-plugin-tutorial

そしてqueryで取得した結果をページに表示するだけでいいのです。

nodeを追加する

nodeにfieldを追加することができます。

creating-slugs-for-pages

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,
    })
  }
}

が追加されてslugが取得できるようになった。

query  {
  allMarkdownRemark {
    edges {
      node {
        id
        fields {
          slug
        }
      }
    }
  }
}

Query

GraphQLを初めて触る人もいるかもしれませんがサイトを作るだけであればレファレンスの内容で十分だと思います。またGrqphiqlで左側をぽちぽち動かせば勝手にquery作ってくれるので特に困ることもないかと思います。

スクリーンショット 2019-12-07 20.36.22.png
alias

allMarkdownRemarkの結果はそのままだとdata.allMarkdownRemarkですが、先頭に任意のaliasをつけることができます。 data.latestPost, data.relatedPostのように帰ってきます。

query {
  latestPost: allMarkdownRemark(limit: 5) { }
  relatedPost: allMarkdownRemark(limit: 5) { }
}
frontmatter

マークダウンファイルの先頭に以下のように情報を定義します。

---
title: my first post
date: 2019-12-7
---

先ほどのgatsby-transformer-remarkを使うことで以下のqueryで取得することができます。

frontmatter {
  title:
  date:
}
params

どちらかというとmarkdown-remarkの話ですが、

  • limit
 allMarkdownRemark(limit: 5) { }
  • format
date(formatString: "YYYY月MM月DD日")
  • filter
filter: { tags: { in: ["post", "page"] }, draft: { eq: false } }
直近の投稿5件を取得するQuery
query {
  allMarkdownRemark(
    limit: 5,
    sort: {order: DESC, fields: frontmatter___create_at}
  ) {
    edges {
      node {
        id
        frontmatter {
          title
        }
      }
    }
  }
}

動的にページを生成する。

ページから必要なデータを取得する

実際にGraphQLからデータを取得してページに表示していきます。

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

export const query = graphql`
  query TitleQuery {
    site {
      siteMetadata {
        title
      }
    }
  }
`
export default ({ data }) => (
  <div>
    <h1>{data.site.siteMetadata.title}</h1>
  </div>
)

page-query

  • 1つのファイルにつき1つのqueryを宣言できる。この時変数は関係ない。
  • queryの結果がpropsのdataオブジェクトに入ってくる。

チュートリアルには書かれていないのですが、上記のルールがあります。

試しに一つのファイルに二つのqueryを宣言してみたら怒られました。

export const query1 = graphql`
  query HomePageQuery {
    site {
      siteMetadata {
        description
      }
    }
  }
`
export const query2 = graphql`
  query HomePageQuery {
    site {
      siteMetadata {
        description
      }
    }
  }
`
Multiple "root" queries found in file

初めてこれを見たときは、どうしてpropsのdataにqueryの結果が渡ってくるんだ?いつqueryは実行されるという気持ちになったのですが、今使っているのは静的サイトジェネレーターでなので、お作法に従ってファイルにqueryを定義すればそれをもとにページ生成してくれるということを忘れていました。

https://www.gatsbyjs.org/docs/recipes/#querying-data-with-a-page-query https://www.gatsbyjs.org/docs/page-query/#add-the-graphql-query

動的にページを生成する

createPage APIを使ってテンプレートから動的にPageを生成します。

チュートリアルのpart7でこんな感じになると思います。

gatsby-node.js
const path = require(`path`)

exports.createPages = async ({ actions, graphql }) => {
  const result = await graphql(`
    {
      allMarkdownRemark {
        edges {
          node {
            frontmatter {
              path
            }
          }
        }
      }
    }
  `)

  if (result.errors) { console.error(result.errors) }

  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    actions.createPage({
      path: node.frontmatter.path,
      component: path.resolve(`src/templates/post.js`),
    })
  })
}
src/templates/post-template.js
import React from "react"
import { graphql } from "gatsby"

export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        path
        title
      }
    }
  }
`

const Template = ({ data }) => {
  const { markdownRemark } = dat
  const { frontmatter, html } = markdownRemark
  return (
    <div className="blog-post">
      <h1>{frontmatter.title}</h1>
      <h2>{frontmatter.date}</h2>
      <div
        className="blog-post-content"
        dangerouslySetInnerHTML={{ __html: html }}
      />
    </div>
  )
}

export default Template;

まず、テンプレートファイルを見てみます。テンプレートファイルはpathパラメータを使ったqueryの結果を埋め込むようになっています。

gatsby-node.jsでは全てのmdファイルのquery結果を元にcreatePageメソッドを読んでいます。 このメソッドが上のテンプレートファイルを指定していて、動的にページが生成されます。 想像はつくもののチュートリアルに書かれていない大事な点は引数のcontextがtemplateのpropsとtemplateファイルのqueryの引数として渡せる点です。

https://www.gatsbyjs.org/docs/recipes/#sourcing-markdown-data-for-blog-posts-and-pages-with-graphql https://www.gatsbyjs.org/docs/using-gatsby-without-graphql/

onCreatePage

All context values are made available to a template’s GraphQL queries as arguments prefaced with $

https://www.gatsbyjs.org/docs/programmatically-create-pages-from-data/#creating-pages

createPage

pased as props to the component this.props.pageContext as well as to the graphql query as graphql arguments.

ソースを探すの少し時間がかかりました。

概念的な

Starter

  • gatsby-starter-default
  • gatsby-starter-blog
  • gatsby-starter-hello-world

公式のstarterは実は上の3つだけ。

https://www.gatsbyjs.org/docs/starters/

スターター一覧 https://www.gatsbyjs.org/starters/?v=2

gatsby new gatsby-starter-hero-blog https://github.com/greglobinski/gatsby-starter-hero-blog

https://www.gatsbyjs.org/starters/greglobinski/gatsby-starter-hero-blog/

これはちょっと重かった。

Plugin

https://www.gatsbyjs.org/docs/plugins/

npm install --save gatsby-transformer-json
module.exports = {
  plugins: [`gatsby-transformer-json`],
}

Pluginは任意のOptionを指定できる。

  plugins: [
    // Shortcut for adding plugins without options.
    "gatsby-plugin-react-helmet",
    {
      // Standard plugin with options example
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/data/`,
        name: "data",
      },
    }
  ]

https://www.gatsbyjs.org/docs/using-a-plugin-in-your-site/

StyledComponentsやaxiosなどのnpx packageを使うには

https://www.gatsbyjs.org/docs/what-you-dont-need-plugins-for/

ローカルのパスを指定してプラグインを入れることもできる。

https://www.gatsbyjs.org/docs/loading-plugins-from-your-local-plugins-folder/

Config

https://www.gatsbyjs.org/docs/api-files-gatsby-node/

gatsby-config.js
gatsyby-node.js

build時に一度だけ呼ばれ、動的にページを生成したり、GraphQLのnodeを追加したりする。

https://www.gatsbyjs.org/docs/api-files-gatsby-node/

実践編

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

styled-componentsを使う

https://www.gatsbyjs.org/docs/styled-components/

モジュールとプラグインを入れます。

npm install --save gatsby-plugin-styled-components styled-components babel-plugin-styled-components
module.exports = {
  plugins: [`gatsby-plugin-styled-components`],
}

typescriptを使う

あまり参考になるかはわかりませんが、Pluginを入れる必要があります。

https://github.com/gatsbyjs/gatsby/tree/master/examples/using-typescript

https://www.gatsbyjs.org/packages/gatsby-plugin-typescript/?=type

npm install gatsby-plugin-typescript
{
  "include": ["./src/**/*"],
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "lib": ["dom", "es2017"],
    "jsx": "react",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "noEmit": true,
    "skipLibCheck": true
  }
}

Typescriptを使うメリットがどれくらいあるかだが、公式がPropsTypesで頑張ってるのをみると入れてもいいと思う。

Syntax Hilight

GatsbyのPluginにのっかって楽をするだけであればプラグイン入れるだけです。

npm i -S gatsby-remark-prismjs

gatsby-transformer-remarkのpluginに追加してあげます。

plugins: [
  {
    resolve: `gatsby-transformer-remark`,
    options: {
      plugins: [
        `gatsby-remark-prismjs`,
      ]
    }
  }
]

先ほどのqueryがclassつきのhtmlを取得できます。 あとは自分でstyleを指定してもいいですが、標準で用意されているテーマを読み込んであげるだけです。

gatsby-browser.js

require("prismjs/themes/prism-solarizedlight.css")

https://www.gatsbyjs.org/packages/gatsby-remark-prismjs/

Tag振り分け

https://www.gatsbyjs.org/docs/adding-tags-and-categories-to-blog-posts/

SEO

SEOコンポネントがドキュメントに掲載されています。

https://www.gatsbyjs.org/docs/add-seo-component/

Deploy(GitHub-Pages)

プラグイン入れてpushするだけです。

https://www.gatsbyjs.org/docs/how-gatsby-works-with-github-pages/

Plugin

エディターのpluginを使えばgraphqlの入力に補間が効くかも・・

感想

簡単簡単と言われているのでQiitaのチュートリアル見ながら30分くらいでいけるのかと思ったのですが、結局ドキュメントをたくさん読むハメになり結構時間がかかってしまいました。 チュートリアルだけではなんで?っていいうお気持ちになるので、余力があればrecipescontent-and-dataをみると理解が早まるのでお勧めです。お作法に従って書くのが苦手でなければかなり簡単だと思います。 というか長々とかきましたがお気に入りのstarterから初めて少し変更するぐらいが一番コスパが良いと思います。

同じタグの投稿

2020 churabou