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
でアクセスできるようになっている。