10月28日追記
Gatsbyブログについて、初noteを書きました。
【完全版】爆速GatsbyでWordPressちっくなブログを作る全手順
こんにちは、筋肉めがねです。
Gatsbyブログのサイドバーに月別アーカイブをつけましたので、そのプロセスをシェアします。
月別アーカイブの良いところは、ブログを書いている人のモチベーションの推移がパッと分かる事ですよね。
僕の場合は、ブログを開始した1月は2日に1記事アップできるペースでしたが、3月、4月と年度をまたぐ時期にペースが落ちています。
6月からは挽回していきたいですね。
月別アーカイブを作る上で参考にさせていただいた記事は以下です。大変感謝です。
Gatsby.jsで年ごと、月ごとで記事一覧を表示したい
まずは、gatsby-node.jsにて、月別のページを作ります。
const _ = require('lodash')
...
exports.createPages = ({ graphql, actions }) => {
const { createPage } = actions
return new Promise((resolve, reject) => {
...
const periodTemplate = path.resolve('./src/templates/period-template.jsx') //後ほどtemplateを作成します。
graphql(`
{
allMarkdownRemark(
limit: 1000
filter: { frontmatter: { draft: { ne: true } } , fields: { draft: { eq: false } } }
sort: { order: DESC, fields: [frontmatter___date] }
) {
edges {
node {
fields {
slug
}
frontmatter {
title
tags
layout
category
year: date(formatString: "YYYY") //Yearの情報を取得します。
month: date(formatString: "MM") //Monthの情報を取得します。
}
}
}
}
}
`).then(result => {
if (result.errors) {
console.log(result.errors)
reject(result.errors)
}
const posts = result.data.allMarkdownRemark.edges
let postsBlog = []
const years = new Set();
const yearMonths = new Set();
posts.forEach((post) => {
if(post.node.fields.slug.includes('/posts')){
postsBlog.push(post)
const { year, month } = post.node.frontmatter;
years.add(year);
yearMonths.add(`${year}/${month}`);
} else{
}
})
// 月別ページ
yearMonths.forEach(yearMonth => {
const [year, month] = yearMonth.split('/')
const startDate = `${year}-${month}-01T00:00:00.000Z`;
const newStartDate = new Date(startDate);
// 月末日を取得
const endDate = new Date(
new Date(newStartDate.setMonth(newStartDate.getMonth() + 1)).getTime() -
1
).toISOString();
createPage({
path: `/${year}/${month}/`,
component: periodTemplate,
context: {
displayYear: year, //displayYearを次のtemplateで使います。
displayMonth: month, //displayMonthも次のtemplateで使います。
periodStartDate: startDate,
periodEndDate: endDate
}
});
});
...
これで、gatsby build時に月毎のページが作られます。 例えば、以下のようなページが作られます。
https://kinnikumegane.com/2020/06
もちろん、period-template.jsxを作成していないので、上のコードだけでは動きません。
続いて、period-template.jsxを作成します。gatsby-node.jsにて読み込むテンプレートですね。
import React from 'react'
import Helmet from 'react-helmet'
import { graphql } from 'gatsby'
import Layout from '../components/Layout'
import Sidebar from '../components/Sidebar'
import PeriodTemplateDetails from '../components/PeriodTemplateDetails'
class PeriodTemplate extends React.Component {
render() {
const { title } = this.props.data.site.siteMetadata
//gatsby-node.jsで設定したぢdisplayMonth, displayYearを読み込みます。
const { displayMonth, displayYear } = this.props.pageContext
return (
<Layout>
<div>
<Helmet title={`${displayYear}年${displayMonth}月の記事 - ${title}`} />
{/* commented out by kinniku <Sidebar {...this.props} /> */}
<PeriodTemplateDetails {...this.props} /> //PeriodTemplateDetails componentはこの次に作成します。
<Sidebar {...this.props} />
</div>
</Layout>
)
}
}
export default PeriodTemplate
export const pageQuery = graphql`
query PeriodPage($periodStartDate: Date, $periodEndDate: Date) {
site {
siteMetadata {
title
subtitle
copyright
menu {
label
path
}
author {
name
twitter
github
}
}
}
allMarkdownRemark(
limit: 1000
filter: {
frontmatter: {
date: { gte: $periodStartDate, lt: $periodEndDate }
layout: { eq: "post" }
draft: { ne: true }
}
}
sort: { order: DESC, fields: [frontmatter___date] }
) {
edges {
node {
fields {
slug
tagSlugs
categorySlug
}
frontmatter {
title
featuredImage {
childImageSharp {
sizes(maxWidth: 630) {
...GatsbyImageSharpSizes
}
}
}
tags
date
category
description
year: date(formatString: "YYYY")
month: date(formatString: "MM")
}
}
}
}
}
`
どんどん行きましょう。
続いて、PeriodTemplateDetails componentを作成します。
1つ前のperiod-template.jsxにて読み込まれるcomponentです。
import React from 'react'
import Post from '../Post'
import './style.scss'
class PeriodTemplateDetails extends React.Component {
render() {
const items = []
const { displayMonth, displayYear } = this.props.pageContext;
const posts = this.props.data.allMarkdownRemark.edges
posts.forEach(post => {
items.push(<Post data={post} key={post.node.fields.slug} />)
})
return (
<div className="content">
<div className="content__inner">
<div className="page">
<h1 className="page__title" id="period_title_area">
<div className="period_title">
<span className="periodMark"></span>
"
{displayYear}年{displayMonth}月の記事
"
</div>
<div className="number_of_post_periodpage">{items.length}件の記事があります</div>
</h1>
<div className="page__body">{items}</div>
</div>
</div>
</div>
)
}
}
export default PeriodTemplateDetails
ここまでで、 一旦gatsby developし、例えば、localhost:8000/2020/01
にアクセスすると、以下のように月ごとのページが表示されます。
続いて、月別アーカイブ(PeriodList)のcomponentを作ります。
// src/components/category-list.js
import React from "react"
import kebabCase from 'lodash/kebabCase'
import './style.scss'
// Components
import { Link, StaticQuery, graphql } from "gatsby"
const PeriodList = () => (
<StaticQuery
query={graphql`
query {
allMarkdownRemark(limit: 2000) {
group(field: frontmatter___date) {
fieldValue
totalCount
}
}
}
`}
render={data => {
const monthGrouppedData = data.allMarkdownRemark.group;
let counts = []
let monthValue = []
for(let i = 0; i< monthGrouppedData.length; i++){
let monthGroupdDateFieldValue = new Date(monthGrouppedData[i].fieldValue)
monthValue.push(monthGroupdDateFieldValue.getMonth());
}
monthValue.forEach(function(x) {
counts[x] = (counts[x] || 0)+1;
});
let counts2 = []
for(let i = 0; i < counts.length; i++){
counts2.push({
number: counts[i],
index: ('0' + (i+1)).slice(-2),
})
}
let reverseCounts = counts2.reverse();
// return JSX
return (
<nav>
<h1 className="periodlist-header">月別アーカイブ</h1>
<ul className="period-list">
{reverseCounts.map(date => (
<li className="period-list-item" key={date.index}>
<span className="period-list-icon"></span>
<Link className="period-list-item-link" to={`/2020/${kebabCase(date.index)}/`}>
2020年{date.index}月({date.number})
</Link>
</li>
))}
</ul>
</nav>
)
}}
/>
)
export default PeriodList
一旦2020をハードコーディングしてますが、ここを変数にできるよう2021年までは必ずブログを続けます。
data.allMarkdownRemark.group
から、年、月のリストを作る方法は幾つかあると思います。
上のコードよりも断然スマートな実装方法がありましたらご連絡いただけますと助かります。
最後です。
SidebarにてPeriodList componentを読み込みます。
...
return (
<div className="sidebar">
<div className="sidebar__inner">
<Adsense />
<div className="sidebar__author">{authorBlock}</div>
<div>
<Menu data={menu} />
...
<Links data={author} />
<CategoryList />
<PeriodList />
<Adsense />
</div>
</div>
...
これで、サイドバーに月別アーカイブが表示されました。