ブログを Hatena から Gatsby の GitHub Pages ホスティングに移行しました。
以下は以降にあたり書き残した雑な技術メモです。
要件
- 記事を GitHub 上で管理できる
- GitHub Pages でホストする
- Blog 記事とホスト用のコードは別の GitHub リポジトリを分離する
技術選定
できる限り労力をかけずにブログを構築できる、かつ記事が多いというところ、また色々使っている人が多かったGatsby
を選択。(React を触ったことがあるので、 というのも一つではあるが。)
セットアップ
環境情報
node -v
# v18.12.1
インストール
まずは、gatsby-cli
を導入し、gatsby を利用できるようにする。
npm install -g gatsby-cli
Blog テンプレートの取得と local での起動
gatsby new blog https://github.com/gatsbyjs/gatsby-starter-blog
cd blog
gatsby develop
gatsby develop
を実行後 http://localhost:8000
で起動する。
画面上では元から用意されているサンプル記事が表示される。
Build と GitHub Pages へのホスト
Build と GitHub Pages へのホストは、Kaname Sasaki 氏のブログ記事を参考にする。
Google Analytics の導入
もともと Hatena でブログを書いており、アクセス解析等がデフォルトでついてることが普通だった。
ただ、自身でホストするとなると、常にそのような機能があるわけではなく、自身で用意する必要がある。そこで、gatsby-plugin-google-gtag
を用いる。
インストールは下記のコマンドでできる。
npm install gatsby-plugin-google-gtag
次に Google Analytics のアカウントを作成し、gatsby-plugin-google-gtag
の設定に追加する。
gatsby-config.js
module.exports = {
plugins: [
// ~~snip~~
{
resolve: `gatsby-plugin-google-gtag`,
options: {
trackingIds: ["G-XXXXXXXXXX"], // Google Analytics のIDを追加
pluginConfig: {
head: true, // headタグに追加する
},
},
},
// ~~snip~~
],
};
目次の追加
ブログを書くと大体長くなるので目次をいい感じに追加してくれるプラグインを追加している。
npm install gatsby-remark-table-of-contents
npm install gatsby-remark-autolink-headers
gatsby-config.js
に先に追加したプラグインの設定を追加する。
// ~~ snip ~~
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
`gatsby-remark-autolink-headers`,
{
resolve: `gatsby-remark-table-of-contents`,
options: {
exclude: "Table of Contents",
tight: false,
ordered: false,
fromHeading: 1,
toHeading: 6,
className: "table-of-contents",
},
},
// ~~ snip ~~
表示用のコンポーネントを作成します。
import React from "react";
const TableOfContents = ({ html }) => {
return <div class="toc" dangerouslySetInnerHTML={{ __html: html }} />;
};
export default TableOfContents;
最後にsrc/templates/blog-post.js
にテーブルを表示するコンポーネントを追加しておしまいです。
src/templates/blog-post.js
import TableOfContents from "../components/tableOfContents"
const BlogPostTemplate = ({
data: { previous, next, site, markdownRemark: post },
location,
}) => {
const siteTitle = site.siteMetadata?.title || `Title`
return (
<Layout location={location} title={siteTitle}>
<article
className="blog-post"
itemScope
itemType="http://schema.org/Article"
>
<header>
<h1 itemProp="headline">{post.frontmatter.title}</h1>
<p>投稿: {post.frontmatter.createDate}</p>
<p>Tags: {post.frontmatter.tags.map(tag => {
return (<Link class="taglink" to={`/tags/${tag}`}>{tag}</Link>)
})}</p>
</header>
<TableOfContents html={post.tableOfContents} />
// ~~ snip ~~
Tag の追加
front matter によるタグと Gatsby のタグ
マークダウンのメタ情報を記述する front matter のタグを元にホストするブログのタグを用意する。
例えば下記の 1 のような情報を front matter に記載していたとして、下記の 2 のような graphql のクエリを Gatsby のページから発行することで、下記の 3 のようにタグを取得することができる。
1. front matter によるメタ情報の記述
---
title: 一年を振り返って
author: Azara / @a_zara_n
tags: ["blog"]
createDate: 2022-12-31 18:04
---
2. Gatsby のページ生成時に Graphql で情報の取得をする
{
markdownRemark {
id
excerpt(pruneLength: 160)
frontmatter {
title
tags
}
}
}
3. 取得される情報
{
"data": {
"markdownRemark": {
"id": "419c83a0-b0e2-5d94-97d3-2d2d69e7491f",
"excerpt": "年末の話 tags: #blog #振り返り はじめに 今年を振り返り 入社して1年 未熟さの実感 一念発起 ビールって美味しい 食わず嫌いをなくそうキャンペーン おわりに --–-",
"frontmatter": {
"title": "一年を振り返って",
"tags": ["blog"]
}
}
},
"extensions": {}
}
タグを貼る
記事にタグを貼ることを考えた際に、次の三点を満たすことを前提に実装を進める。
- タグがつけられた記事の一覧を表示する
- トップページに Tag を表示する
- 最小限で実装したいので、多くなった場合のことはこの際考慮しない
- 記事か 1 のページに遷移できる
タグに紐づけられた記事一覧ページ
タグに紐づけられた記事一覧ページを実装するにあたり、2 つのファイルを編集する。1 つは既存のgatsby-node.js
、もう 1 つは作成するsrc/templates/tag.js
のファイルになる。
src/templates/tag.js
は記事一覧の描画を担当するファイルで、下記のような実装をした。
import React from "react";
import PropTypes from "prop-types";
// Components
import { Link, graphql } from "gatsby";
import Layout from "../components/layout";
const Tags = ({ pageContext, data }) => {
const siteTitle = data.site.siteMetadata?.title || `Title`;
const { tag } = pageContext;
const { edges, totalCount } = data.allMarkdownRemark;
const tagHeader = `${totalCount} post${
totalCount === 1 ? "" : "s"
} tagged with "${tag}"`;
return (
<Layout location title={siteTitle}>
<article
className="blog-post"
itemScope
itemType="http://schema.org/Article"
>
<header>
<h1 itemProp="headline">{tagHeader}</h1>
</header>
<ol style={{ listStyle: `none` }}>
{edges.map(({ node }) => {
const { slug } = node.fields;
const { title } = node.frontmatter;
return (
<li key={slug}>
<article
className="post-list-item"
itemScope
itemType="http://schema.org/Article"
>
<header>
<h2>
<Link to={slug} itemProp="url">
<span itemProp="headline">{title}</span>
</Link>
</h2>
</header>
<section>
<p
dangerouslySetInnerHTML={{
__html: node.excerpt || "aaaa",
}}
itemProp="description"
/>
</section>
</article>
</li>
);
})}
<Link to="/tags">All tags</Link>
</ol>
</article>
</Layout>
);
};
Tags.propTypes = {
pageContext: PropTypes.shape({
tag: PropTypes.string.isRequired,
}),
data: PropTypes.shape({
allMarkdownRemark: PropTypes.shape({
totalCount: PropTypes.number.isRequired,
edges: PropTypes.arrayOf(
PropTypes.shape({
node: PropTypes.shape({
frontmatter: PropTypes.shape({
title: PropTypes.string.isRequired,
}),
fields: PropTypes.shape({
slug: PropTypes.string.isRequired,
}),
}),
}).isRequired
),
}),
}),
};
export default Tags;
export const pageQuery = graphql`
query ($tag: String) {
site {
siteMetadata {
title
}
}
allMarkdownRemark(
limit: 2000
sort: { frontmatter: { date: ASC } }
filter: { frontmatter: { tags: { in: [$tag] } } }
) {
totalCount
edges {
node {
fields {
slug
}
frontmatter {
title
}
excerpt
}
}
}
}
`;
gatsby-node.js
は、先に作ったテンプレートを展開し、専用のページを作成に関連するコードを書いていく。
// ~~snip~~
const tagPage = path.resolve(`./src/templates/tag.js`);
/**
* @type {import('gatsby').GatsbyNode['createPages']}
*/
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions;
// Get all markdown blog posts sorted by date
const result = await graphql(`
{
allMarkdownRemark(sort: { frontmatter: { date: ASC } }, limit: 1000) {
nodes {
id
fields {
slug
}
frontmatter {
tags
}
}
}
tagsGroup: allMarkdownRemark(limit: 1000) {
group(field: { frontmatter: { tags: SELECT } }) {
fieldValue
}
}
}
`);
// ~~snip~~
const tags = result.data.tagsGroup.group;
tags.forEach((tag) => {
createPage({
path: `/tags/${tag.fieldValue}/`,
component: tagPage,
context: {
tag: tag.fieldValue,
},
});
});
};
// ~~snip~~
記事に Tag を表示する
記事に付与されたタグを表示し、関連記事の一覧ページに移動できるようにする。 src/templates/blog-post.js
// ~~snip~~
<p>
Tags:{" "}
{post.frontmatter.tags.map((tag) => {
return (
<Link class="taglink" to={`/tags/${tag}`}>
{tag}
</Link>
);
})}
</p>
// ~~snip~~
GitHub Pages へのデプロイ
GitHub Pages へのデプロイは Blog 側のリポジトリで main ブランチへの push があった際に GitHub actions を動作させ、一連の動作ののちに、gh-pages
を用いて GitHub Pages へデプロイしています。
インストール
npm install gh-pages
参考
- Gatsby と GitHub Pages で作る Markdown ブログ | Blog
- Gatsby.js に Google Analytics を導入する
- Gatsby 製のブログにタグ機能を追加するための方法を見直してみよう | blog.ojisan.io
- Gatsby に目次を追加する | Glatch Tech
- Gatsby(GatsbyJS)で SNS の SHARE を設置 | Gatsby ブログカスタマイズ | フロントセンセイ
Obsidian Tags : #blog #Gatsby