Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
59
Help us understand the problem. What is going on with this article?
@abcb2

Gatsbyを使ってみる

More than 3 years have passed since last update.

Gatsbyとは

Reactを使った静的サイト生成ツール
static PWA(Progressive Web Application)を作れるらしい。PWAとはネイティブアプリのようなWEBアプリと自分は解釈している。

Reactのことをさっぱり分かっていないこともあり、学習も兼ねて使ってみた。
静的サイト生成ツールはこちらの一覧が参考になった。

markdownを書いてblog記事にするためのhow to

1. インストールと設定

gatsby-plugin-react-helmetが2系だと、$ gatsby developでエラー出てたけどもう直ってるかも?

$ node -v 
v9.3.0
$ npm install -g gatsby-cli
$ gatsby new my-blog
$ cd my-blog
$ npm install gatsby-source-filesystem gatsby-transformer-remark
$ npm ls -g --depth=0
/home/kw/.nvm/versions/node/v9.3.0/lib
├── gatsby-cli@1.1.28
└── npm@5.5.1
$ npm ls --depth=0
gatsby-starter-default@1.0.0 /path/to/my-blog
├── gatsby@1.9.145
├── gatsby-link@1.6.32
├── gatsby-plugin-react-helmet@1.0.8
├── gatsby-source-filesystem@1.5.11
├── gatsby-transformer-remark@1.7.26
└── prettier@1.9.2

gatsby-config.jsを下記のように編集

module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    `gatsby-transformer-remark`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `src`,
        path: `${__dirname}/src`,
      },
    },
  ],
};

2. markdownでブログ記事を書く

$ cd src/pages
$ mkdir -p 2018/01/14
$ touch 2018/01/14/hello-my-blog.md

hello-my-blog.mdは次のように記述する


---
path: "/2018/01/14/hello-my-blog"
date: "2018-01-14T22:35:00Z"
title: "Gatsbyとgithub pagesでブログ"
tags: ['gatsby', 'react']
excerpt: "Gatsbyとgithub pagesでブログを作成してみた"
---

# Gatsbyとは
.
.
.

3. index.jsを修正する

src/pages/index.jsを修正する

import React from 'react';
import Link from 'gatsby-link';

const IndexPage = ({data}) => {
  const {edges: posts} = data.allMarkdownRemark;
  return (
      <div>
        {posts.map(({node: post}) => {
          const {frontmatter} = post;
          return (
              <div key={post.id}>
                <h2>
                  <Link to={frontmatter.path}>
                    {frontmatter.title}
                  </Link>
                </h2>
                <p>{frontmatter.date}</p>
                <p>{frontmatter.excerpt}</p>
              </div>
          );
        })}
      </div>
  );
};

export const query = graphql`
  query IndexQuery {
    allMarkdownRemark {
      totalCount
      edges {
        node {
          id
          frontmatter {
            title
            date(formatString: "MMMM DD, YYYY")
            path
            tags
            excerpt
          }
        }
      }
    }
  }
`;

export default IndexPage;

localサーバーを起動して確かめる

localhost:8000にアクセスできる

$ gatsby develop

4. Blogコンテンツ内容を表示するテンプレートの作成

blog記事の一覧をindexで表示することはできたが、個別のページへのリンクをクリックすると404のままなので、テンプレートを作成する。

$ pwd
src
$ ls
layouts pages
$ mkdir templates
$ touch templates/blog-post.js

blog-post.jsを編集して次のようにする

import React from 'react';
import Link from 'gatsby-link';
import Helmet from 'react-helmet';

const Template = ({data, location, pathContext}) => {
  const {markdownRemark: post} = data;
  const {frontmatter, html} = post;
  const {title, date} = frontmatter;

  return (
      <div>
        <Helmet title={`${frontmatter.title} - My Blog`}/>
        <div>
          <h1>{title}</h1>
          <h3>{date}</h3>
          <div dangerouslySetInnerHTML={{__html: html}}/>
        </div>
      </div>
  );
};

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

export default Template;

5. Dynamicにコンテンツを生成する

gatsby-node.jsを編集して次のようにする。

/**
 * Implement Gatsby's Node APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/node-apis/
 */
const path = require('path');

exports.createPages = ({boundActionCreators, graphql}) => {
  const {createPage} = boundActionCreators;
  const blogPostTemplate = path.resolve(`src/templates/blog-post.js`);

  return graphql(`{
    allMarkdownRemark {
      edges {
        node {
          html
          id
          frontmatter {
            date
            path
            title
            excerpt
            tags
          }
        }
      }
    }
  }`).then(result => {
    if (result.errors) {
      return Promise.reject(result.errors);
    }
    const posts = result.data.allMarkdownRemark.edges;

    posts.forEach(({node}, index) => {
      createPage({
        path: node.frontmatter.path,
        component: blogPostTemplate,
      });
    });
  });
};

これでblog記事へのリンクをクリックするとコンテンツが表示される

$ gatsby develop

6. 前後の記事へのリンクをつくる

blog-post.jsは下記のように修正

import React from 'react';
import Link from 'gatsby-link';
import Helmet from 'react-helmet';

const Template = ({data, location, pathContext}) => {
  const {markdownRemark: post} = data;
  const {frontmatter, html} = post;
  const {title, date} = frontmatter;
  const {next, prev} = pathContext;

  return (
      <div>
        <Helmet title={`${frontmatter.title} - My Blog`}/>
        <div>
          <h1>{title}</h1>
          <h3>{date}</h3>
          <div dangerouslySetInnerHTML={{__html: html}}/>
          <p>
            {prev && (
                <Link to={prev.frontmatter.path}>
                  Previous: {prev.frontmatter.title}
                </Link>
            )}
          </p>
          <p>
            {next && (
                <Link to={next.frontmatter.path}>
                  Next: {next.frontmatter.title}
                </Link>
            )}
          </p>
        </div>
      </div>
  );
};

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

export default Template;

gatsby-node.jsは次のように修正

/**
 * Implement Gatsby's Node APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/node-apis/
 */
const path = require('path');

exports.createPages = ({boundActionCreators, graphql}) => {
  const {createPage} = boundActionCreators;
  const blogPostTemplate = path.resolve(`src/templates/blog-post.js`);

  return graphql(`{
    allMarkdownRemark {
      edges {
        node {
          html
          id
          frontmatter {
            date
            path
            title
            excerpt
            tags
          }
        }
      }
    }
  }`).then(result => {
    if (result.errors) {
      return Promise.reject(result.errors);
    }
    const posts = result.data.allMarkdownRemark.edges;

    posts.forEach(({node}, index) => {
      createPage({
        path: node.frontmatter.path,
        component: blogPostTemplate,
        context: {
          prev: index === 0 ? null : posts[index - 1].node,
          next: index === (posts.length - 1) ? null : posts[index + 1].node,
        },
      });
    });
  });
};

7. tag一覧を作る

テンプレートを作成する

$ pwd
blog/src
$ ls
layouts pages templates
$ touch templates/all-tags.js
$ touch templates/tags.js

all-tags.jsは下記のように

import React from 'react';
import Link from 'gatsby-link';

const AllTags = ({pathContext}) => {
  const {tags} = pathContext;

  if (tags) {
    return (
        <div>
          <ul>
            {tags.map(tag => {
              return (
                  <li>
                    <Link to={`/tags/${tag}`}>
                      {tag}
                    </Link>
                  </li>
              );
            })}
          </ul>
        </div>
    );
  }
};

export default AllTags;

tags.jsは次のように

import React from 'react';
import Link from 'gatsby-link';

const Tags = ({pathContext}) => {
  const {posts, tagName} = pathContext;

  if (posts) {
    return (
        <div>
          <span>
            Posts abount {tagName}:
          </span>
          <ul>
            {posts.map(post => {
              return (
                  <li>
                    <Link to={post.frontmatter.path}>
                      {post.frontmatter.title}
                    </Link>
                  </li>
              );
            })}
          </ul>
        </div>
    );
  }
};

export default Tags;

gatsby-node.jsは次のようにtagに関する記述を追加する

/**
 * Implement Gatsby's Node APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/node-apis/
 */
const path = require('path');

const createTagPages = (createPage, posts) => {
  const tagPageTemplate = path.resolve(`src/templates/tags.js`);
  const allTagsTemplate = path.resolve(`src/templates/all-tags.js`);

  const postsByTags = {};

  posts.forEach(({node}) => {
    if (node.frontmatter.tags) {
      node.frontmatter.tags.forEach(tag => {
        if (!postsByTags[tag]) {
          postsByTags[tag] = [];
        }
        postsByTags[tag].push(node);
      });
    }
  });

  const tags = Object.keys(postsByTags);

  createPage({
    path: `/tags`,
    component: allTagsTemplate,
    context: {
      tags: tags.sort(),
    },
  });

  tags.forEach(tagName => {
    const posts = postsByTags[tagName];

    createPage({
      path: `/tags/${tagName}`,
      component: tagPageTemplate,
      context: {
        posts,
        tagName,
      },
    });
  });
};

exports.createPages = ({boundActionCreators, graphql}) => {
  const {createPage} = boundActionCreators;
  const blogPostTemplate = path.resolve(`src/templates/blog-post.js`);

  return graphql(`{
    allMarkdownRemark {
      edges {
        node {
          html
          id
          frontmatter {
            date
            path
            title
            excerpt
            tags
          }
        }
      }
    }
  }`).then(result => {
    if (result.errors) {
      return Promise.reject(result.errors);
    }
    const posts = result.data.allMarkdownRemark.edges;

    createTagPages(createPage, posts);

    posts.forEach(({node}, index) => {
      createPage({
        path: node.frontmatter.path,
        component: blogPostTemplate,
        context: {
          prev: index === 0 ? null : posts[index - 1].node,
          next: index === (posts.length - 1) ? null : posts[index + 1].node,
        },
      });
    });
  });
};

さらにpages/index.jsを修正

import React from 'react';
import Link from 'gatsby-link';

const IndexPage = ({data}) => {
  const {edges: posts} = data.allMarkdownRemark;
  return (
      <div>
        {posts.map(({node: post}) => {
          const {frontmatter} = post;
          return (
              <div key={post.id}>
                <h2>
                  <Link to={frontmatter.path}>
                    {frontmatter.title}
                  </Link>
                </h2>
                <p>{frontmatter.date}</p>
                <p>{frontmatter.excerpt}</p>
                <ul key={post.id}>
                  {post.frontmatter.tags.map(tag => {
                    return (
                        <li>
                          <Link to={`/tags/${tag}`}>
                            {tag}
                          </Link>
                        </li>
                    );
                  })}
                </ul>
              </div>
          );
        })}
      </div>
  );
};

export const query = graphql`
  query IndexQuery {
    allMarkdownRemark {
      totalCount
      edges {
        node {
          id
          frontmatter {
            title
            date(formatString: "MMMM DD, YYYY")
            path
            tags
            excerpt
          }
        }
      }
    }
  }
`;

export default IndexPage;

8. github pagesにdeployする

モジュールを追加する

$ npm install --save gh-pages

package.jsonを修正する

.
.
"dependencies": {
    "gatsby": "^1.9.145",
    "gatsby-link": "^1.6.32",
    "gatsby-plugin-react-helmet": "^1.0.8",
    "gatsby-source-filesystem": "^1.5.11",
    "gatsby-transformer-remark": "^1.7.26",
    "gh-pages": "^1.1.0"
  },
  "keywords": [
    "gatsby"
  ],
  "license": "MIT",
  "main": "n/a",
  "scripts": {
    "build": "gatsby build",
    "deploy": "gatsby build --prefix-paths && gh-pages -d public",
    "develop": "gatsby develop",
    "format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"src/**/*.js\"",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
.
.

deployコマンドが追加されている。

github pagesを使ってサイトを作るにはレポジトリを作成して設定からgithub pagesの設定をすることで作成可能になる。

プロジェクト名がパス名になるので、このプロジェクトの名前はblogだ。(private repoにしているがgithub pagesは利用可能だ)

デプロイするには下記のコマンドを実行する

$ npm run-script deploy

でアクセスできるようになっている。

59
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
abcb2

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
59
Help us understand the problem. What is going on with this article?