Posted at

Gatsbyを使ってみる

More than 1 year has 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

https://abcb2.github.io/blog

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