26
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GatsbyjsでWordPress風の簡易ブログを作って勉強してみた

Last updated at Posted at 2020-01-04

Gatsbyjsのチュートリアルがとてもわかりやすくて勉強になったので、
自分で内容を忘れないためにWordPressのデフォルトのテンプレートに近いものを作ってみました

また、私はGatsbyjsやWordPress初心者です、よろしくお願いいたします :bow:


作ったのはGitHub Pagesで見れます↓

Screen Shot 2020-01-04 at 21.45.46.png


私が作成したコードは以下です

0. gatsbyの雛形を作成

bash
npx -p gatsby-cli gatsby new myblog https://github.com/gatsbyjs/gatsby-starter-hello-world
cd myblog
yarn develop

http://localhost:8000/ で表示される

Screen Shot 2020-01-04 at 22.38.12.png

1. 全体のスタイルを設定

src/styles/global.css
body {
    font-family: Verdana, sans-serif;
    margin: 0 auto;
    width: 900px;
}

a {
    text-decoration: none;
}

gatsby-browser.jsで読み込む

gatsby-browser.js
import "./src/styles/global.css"

gatsby-browser.jsなどの設定関連のファイルは、変更するたびにCtrl+Cで開発サーバーを終了し、再度yarn developで起動し直さないと反映されないようでした

2. ヘッダー、フッター、サイドバーを作成して共通化

src/index.jsの内容を以下のように変更、(※拡張子もjsxに変更しました)

src/pages/index.jsx
import React from "react"
import Layout from "../components/Layout"

export default () => (
  <Layout page={"top"}>
    <div>Hello world!</div>
  </Layout>
)

共通化したのがわかりやすいように、もう一つページを作成する

src/pages/sample-page.jsx
import React from "react"
import Layout from "../components/Layout"

export default () => (
  <Layout>
    <h1>サンプルページ</h1>
    <p>
      これはサンプルページです。同じ位置に固定され、(多くのテーマでは)
      サイトナビゲーションメニューに含まれる点がブログ投稿とは異なります。まずは、サイト訪問者に対して自分のことを説明する自己紹介ページを作成するのが一般的です。たとえば以下のようなものです。
    </p>
    <blockquote class="wp-block-quote">
      <p>
        はじめまして。昼間はバイク便のメッセンジャーとして働いていますが、俳優志望でもあります。これは僕のサイトです。ロサンゼルスに住み、ジャックという名前のかわいい犬を飼っています。好きなものはピニャコラーダ、そして通り雨に濡れること。
      </p>
    </blockquote>
  </Layout>
)

ヘッダーで読み込む用にサイトのメタデータを設定しておく

gatsby-config.js
module.exports = {
  siteMetadata: {
    title: "ホゲホゲブログ",
    description: "ホゲホゲによるブログです",
  },
}

全体のパーツを読み込むファイル読み込むファイル

src/components/Layout.jsx
import React from "react"
import Header from "./Header"
import Main from "./Main"
import Sidebar from "./Sidebar"
import Footer from "./Footer"

export default ({ page = "default", children }) => (
  <>
    <Header page={page} />
    <Main sidebar={<Sidebar />}>{children}</Main>
    <Footer />
  </>
)

ヘッダーを表示するファイル。
GraphQLを使ってサイトのメタデータを取得します。

src/components/Header.jsx
import React from "react"
import { useStaticQuery, Link, graphql } from "gatsby"
import styles from "../styles/header.module.css"

export default ({ page }) => {
  const {
    site: {
      siteMetadata: { title, description },
    },
  } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
          }
        }
      }
    `
  )

  return (
    <header>
      {page === "top" && <div className={styles.headerImg}>&nbsp;</div>}
      <div className={styles.headerBox}>
        <h1>
          <Link to="/">{title}</Link>
        </h1>
        <p>{description}</p>
      </div>
    </header>
  )
}

サイドバーとメインコンテンツを表示するファイル

src/components/Main.jsx
import React from "react"
import styles from "../styles/main.module.css"

export default ({ children, sidebar }) => (
  <div className={styles.flex}>
    <div className={styles.mainLeft}>{children}</div>
    <div className={styles.mainRight}>{sidebar}</div>
  </div>
)

サイドバー表示、「最近の投稿」と「カテゴリー」の表示はあとで行います

src/components/Sidebar.jsx
import React from "react"
import { Link } from "gatsby"

export default () => {
  return (
    <>
      <div>
        <h3>固定ページ</h3>
        <ul>
          <li>
            <Link to="/sample-page/">サンプルページ</Link>
          </li>
        </ul>
      </div>

      <div>
        <h3>最近の投稿</h3>
        <ul></ul>
      </div>

      <div>
        <h3>カテゴリー</h3>
        <ul></ul>
      </div>
    </>
  )
}

フッターを表示するファイル

src/components/Footer.jsx
import React from "react"
import { Link } from "gatsby"

export default () => (
  <>
    <hr />
    <Link to="https://www.gatsbyjs.org/">Powered by Gatsbyjs</Link>
  </>
)

共通部分の作成までできました

fcD99uOlTM.gif

3. トップページにブログ一覧表示

blogフォルダにマークダウンで内容を書いて、それを一覧表示します。
マークダウンの内容はこのような感じです。

blog/hello-world.md
---
title: Hello World
date: "2017-08-21"
categories: []
---

Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。

Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。

Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。

ローカルファイルのデータをGatsbyに渡せるプラグインと、
マークダウンを扱うプラグインをインストール

yarn add gatsby-source-filesystem gatsby-transformer-remark

プラグインを使えるように設定

gatsby-config.js
module.exports = {
  siteMetadata: {
    title: "ホゲホゲブログ",
    description: "ホゲホゲによるブログです",
  },
  plugins: [
    {
      resolve: "gatsby-source-filesystem",
      options: {
        path: `${__dirname}/blog`,
        name: "blog",
      },
    },
    "gatsby-transformer-remark",
  ],
}

gatsby-node.js を作成して、createNodeField でslugを設定します、これでGraphQLからslugが取得できるようになります。

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

↑詳しい説明はチュートリアルの、パート6、7あたりです。


GraphQLで、日付でソートしつつ必要なデータを取得しました

src/pages/index.jsx
import React from "react"
import { Link, graphql } from "gatsby"
import Layout from "../components/Layout"
import styles from "../styles/index.module.css"

export default ({
  data: {
    allMarkdownRemark: { totalCount, edges: blogs },
  },
}) => (
  <Layout page={"top"}>
    <strong>投稿 ( {totalCount} ) </strong>
    {blogs.map(
      ({
        node: {
          id,
          frontmatter: { title, date },
          fields: { slug },
          excerpt,
        },
      }) => (
        <div key={id} className={styles.box}>
          <div className={styles.date}>{date}</div>
          <h2 className={styles.title}>
            <Link to={slug}>{title}</Link>
          </h2>
          <p>{excerpt}</p>
        </div>
      )
    )}
  </Layout>
)

export const query = graphql`
  query {
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      totalCount
      edges {
        node {
          id
          frontmatter {
            title
            date(formatString: "YYYY年MM月DD日")
          }
          fields {
            slug
          }
          excerpt
        }
      }
    }
  }
`

これによりトップページにブログの一覧表示はできましたが、まだ詳細表示するページを作成していないためアクセスしても404と表示されます。

wcBbnWmC0G.gif

4. ブログ詳細ページ作成

ブログページを表示するためのテンプレートを作成しておきます

src/templates/blog-post.jsx
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"

export default ({
  data: {
    markdownRemark: {
      html,
      frontmatter: { title, categories },
    },
  },
}) => {
  return (
    <Layout>
      <h1>{title}</h1>
      <>
        <strong>カテゴリー : </strong>
        {categories.length ? categories.join(", ") : "なし"}
      </>
      <div dangerouslySetInnerHTML={{ __html: html }} />
    </Layout>
  )
}

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

gatsby-node.js を変更して、ブログページが作成されるようにします

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.jsx"),
      context: {
        // GraphQLのクエリの $slug 変数に設定する値
        slug: node.fields.slug,
      },
    })
  })
}

ブログのページを確認できました

Screen Shot 2020-01-05 at 0.10.52.png

5. カテゴリーごとの一覧ページ作成

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

先ほどと同じようにテンプレートを作成します

src/templates/categories.jsx
import React from "react"
import { Link, graphql } from "gatsby"
import Layout from "../components/layout"

export default ({
  data: {
    allMarkdownRemark: { totalCount, edges: blogs },
  },
}) => {
  return (
    <Layout>
      {blogs.map(
        ({
          node: {
            id,
            html,
            frontmatter: { title },
            fields: { slug },
          },
        }) => (
          <div key={id}>
            <h2>
              <Link to={slug}>{title}</Link>
            </h2>
            <div dangerouslySetInnerHTML={{ __html: html }} />
            <hr />
          </div>
        )
      )}
    </Layout>
  )
}

export const pageQuery = graphql`
  query($category: String!) {
    allMarkdownRemark(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { categories: { in: [$category] } } }
    ) {
      totalCount
      edges {
        node {
          id
          html
          frontmatter {
            title
          }
          fields {
            slug
          }
        }
      }
    }
  }
`

gatsby-node.jsもブログの詳細ページを作成した時と同じようにします

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
              }
            }
          }
        }
+       categoriesAllMarkdownRemark: allMarkdownRemark {
+         group(field: frontmatter___categories) {
+           category: fieldValue
+         }
+       }
      }
    `)
    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
      createPage({
        path: node.fields.slug,
        component: path.resolve("./src/templates/blog-post.jsx"),
        context: {
          // GraphQLのクエリの $slug 変数に設定する値
          slug: node.fields.slug,
        },
      })
    })
+   result.data.categoriesAllMarkdownRemark.group.forEach(({ category }) => {
+     createPage({
+       path: `/category/${category}/`,
+       component: path.resolve("./src/templates/categories.jsx"),
+       context: {
+         // GraphQLのクエリの $category 変数に設定する値
+         category,
+       },
+     })
+   })
  }

まだページ内からのリンクはないのですが、http://localhost:8000/category/ブログ/とurlを直打ちすると、カテゴリーに対応する投稿のみを表示することができました

Screen Shot 2020-01-05 at 0.18.36.png

6. サイドバーの情報を取得して表示

Sidebar.jsxをGraphQLからデータを取ってくるように変更

src/components/Sidebar.jsx
import React from "react"
import { useStaticQuery, Link, graphql } from "gatsby"

export default () => {
  const {
    recentlyAllMarkdownRemark: { edges: recentlyBlogs },
    categoriesAllMarkdownRemark: { group: categories },
  } = useStaticQuery(
    graphql`
      query {
        recentlyAllMarkdownRemark: allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: DESC }
          limit: 5
        ) {
          edges {
            node {
              id
              frontmatter {
                title
              }
              fields {
                slug
              }
            }
          }
        }
        categoriesAllMarkdownRemark: allMarkdownRemark {
          group(field: frontmatter___categories) {
            category: fieldValue
            totalCount
          }
        }
      }
    `
  )

  return (
    <>
      <div>
        <h3>固定ページ</h3>
        <ul>
          <li>
            <Link to="/sample-page/">サンプルページ</Link>
          </li>
        </ul>
      </div>

      <div>
        <h3>最近の投稿</h3>
        <ul>
          {recentlyBlogs.map(
            ({
              node: {
                id,
                frontmatter: { title },
                fields: { slug },
              },
            }) => (
              <li key={id}>
                <Link to={slug}>{title}</Link>
              </li>
            )
          )}
        </ul>
      </div>

      <div>
        <h3>カテゴリー</h3>
        <ul>
          {categories.map(({ category, totalCount }) => (
            <li key={category}>
              <Link to={`category/${category}`}>{category}</Link> ( {totalCount}{" "}
              )
            </li>
          ))}
        </ul>
      </div>
    </>
  )
}

サイドバーも表示することができました

Screen Shot 2020-01-05 at 0.20.30.png

7. サイトメタデータ

チュートリアルのパート8参考
https://www.gatsbyjs.org/tutorial/part-eight/

サイトのmetaタグやtitledescriptionを設定します

react-helmetを使うのでインストール

yarn add gatsby-plugin-react-helmet react-helmet
gatsby-config.js
  module.exports = {
    siteMetadata: {
      title: "ホゲホゲブログ",
      description: "ホゲホゲによるブログです",
    },
    plugins: [
      {
        resolve: "gatsby-source-filesystem",
        options: {
          path: `${__dirname}/blog`,
          name: "blog",
        },
      },
      "gatsby-transformer-remark",
+     "gatsby-plugin-react-helmet",
    ],
  }
src/components/Header.jsx
  import React from "react"
+ import Helmet from "react-helmet"
  import { useStaticQuery, Link, graphql } from "gatsby"
  import styles from "../styles/header.module.css"

  export default ({ page }) => {
    const {
      site: {
        siteMetadata: { title, description },
      },
    } = useStaticQuery(
      graphql`
        query {
          site {
            siteMetadata {
              title
              description
            }
          }
        }
      `
    )

    return (
+     <>
+       <Helmet
+         htmlAttributes={{ lang: "ja" }}
+         title={title}
+         meta={[
+           {
+             name: "description",
+             content: description,
+           },
+         ]}
+       />
        <header>
          {page === "top" && <div className={styles.headerImg}>&nbsp;</div>}
          <div className={styles.headerBox}>
            <h1>
              <Link to="/">{title}</Link>
            </h1>
            <p>{description}</p>
          </div>
        </header>
+     </>
    )
  }

タイトルタグや、metaタグが設定できました。

Screen Shot 2020-01-05 at 0.32.47.png

最後まで読んでいただいてありがとうございました。m(_ _)m

26
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?