今回でGatsbyカスタマイズ10記事!せっかく書いたブログ記事、たくさん読んで欲しいですよね?そこでブログ詳細での関連記事出力のコンポーネントを作ります!
関連記事をランダムで6記事ピックアップして出力します。
※ 2021年12月アップデートに伴い、v4対応済みです。
かみーゆ/フロントエンドエンジニア
今までのGatsbyの記事と注意点
現在ここまで記載しています。
制作するまでを目標にUPしていくので順を追ったらGatsbyサイトが作れると思います。
- インストールからNetlifyデプロイまで
- ヘッダーとフッターを追加する
- 投稿テンプレにカテゴリやらメインビジュアル(アイキャッチ)追加
- ブログ記事、カテゴリ、タグ一覧の出力
- プラグインを利用して目次出力
- プラグインナシで一覧にページネーション実装
- 個別ページテンプレート作成
- プラグインHelmetでSEO調整
- CSSコンポーネントでオリジナルページを作ろう!!
- 関連記事一覧出力(←イマココ)
- タグクラウドコンポーネントを作成する
- パンくずリストを追加する
Gatsbyのv4からv5へアップグレードしたのでそのやり方をメモしておきます。Netlify の Nodeバージョンの...
このシリーズはGithub・gatsby-blogに各内容ブランチごとで分けて格納しています。
今回のソースはrelated-listブランチにあります。
このシリーズではテーマGatsby Starter Blogを改造
この記事は一番メジャーなテンプレート、「Gatsby Starter Blog」を改造しています。同じテーマでないと動かない可能性があります。
StaticQueryタグを使って同じタグとカテゴリの記事を絞り込む
類似記事を絞り込む条件です。
- タイトルがかぶっていない
- カテゴリが一緒
- もしくは、タグが一緒
上記の条件の記事をPickupしランダムで6記事出力します。
今回触るファイルです。relatedList.jsを追加します。
/プロジェクト
├ components/
| └ related-list.js(追加)
└ templates/
└ blog-post.js(追記)
絞り込み機能を作る
useStaticQuery
を使ってGraphQLで出力したい内容のスキーマを書きます。
import * as React from "react"
import { Link, useStaticQuery, graphql } from "gatsby"
const Lists = ({ category, title, tags }) => {
const { allMarkdownRemark } = useStaticQuery(
graphql`
query {
allMarkdownRemark (
filter: { frontmatter: { pagetype: { eq: "blog" } } }
) {
edges {
node {
fields {
slug
}
frontmatter {
cate
hero
date(formatString: "YYYY.MM.DD")
title
tags
}
}
}
}
}
`
)
return ""
}
export default Lists
「Gatsbyブログサイト移行物語」では記事にはタグとカテゴリを設けており、カテゴリは1種類、タグは複数選択可能 というルールになっています。
あらかじめ関連記事を表示するコンポーネントを読み込んでおきます。
import * as React from "react"
import { Link, graphql } from "gatsby"
// 省略
import RetatedList from "../components/retated-list"
const BlogPostTemplate = ({ data, location }) => {
// 省略
return (
<Layout location={location} title={siteTitle}>
{/*省略*/}
<BlogPostNav>
{/*省略*/}
</BlogPostNav>
<RetatedList category={cate} slug={post.fields.slug} tags={tags} ></RetatedList> </Layout>
)
}
export default BlogPostTemplate
// 省略
関連記事では現在表示中の記事をのぞきたいので、blog-list.jsのGraghQLのスキーマにslugを追加します。
# 省略
markdownRemark(id: { eq: $id }) {
id
excerpt(pruneLength: 160)
html
tableOfContents(maxDepth: 3)
fields { slug } # 省略
filter
で記事を絞り込んだ配列を作ります。
let posts = allMarkdownRemark.edges.filter(post => {
// 表示中の記事とslugが一緒じゃないものを絞り込み
if (post.node.fields.slug !== slug) {
// カテゴリが一緒かつ
if (post.node.frontmatter.cate === category) {
return true
}
// 同じタグを含むもの
for (const tag of tags) {
return post.node.frontmatter.tags.includes(tag)
}
}
})
関連記事が存在したらランダム出力する
配列をシャッフルし6記事に絞り込みます。
// 一致するものがなければ処理しない
if (!posts) return
// 関連記事が取得できたらシャッフル
function shuffle(list) {
var i = list.length;
while (--i) {
var j = Math.floor(Math.random() * (i + 1));
if (i === j) continue;
var k = list[i];
list[i] = list[j];
list[j] = k;
}
return list;
}
// 関数実行
shuffle(posts)
// 6記事に絞りこむ
posts = posts.slice(0, 6);
結果を出力するコンポーネント作成
抽出結果をコンポーネントを通じて出力します。とりあえずは最小のデータで確かめてみます。
return (
<section>
<h2>関連記事もあわせてお読みください</h2>
<div>
{posts.map(item => {
return <List item={item.node.frontmatter} url={item.node.fields.slug} />
})
}
</div>
</section>
)
見た目が貧弱なので、そのほかのデータを出力します。
サムネ画像出力用コンポーネントを追加し、スタイルはプラグインstyled-componentsでスタイル用コンポーネントを作ります。
サムネ画像出力用コンポーネントの実装方法は「ブログ記事、カテゴリ、タグ一覧の出力」を参考にしてください。
styled-componentsは以下コマンドでインストール可能です。
npm i styled-components
styled-componentsを追加。
// 省略
import Img from "../components/img"
import styled from "styled-components"
const Lists = ({ category, slug, tags }) => {
// 省略
export default Lists
関連記事を出力するコンポーネントのコードをまとめて書く
スタイルも当ててまとめたコードがこちらです。
import * as React from "react"
import { Link, useStaticQuery, graphql } from "gatsby"
import Img from "../components/img"
import styled from "styled-components"
const Lists = ({ category, slug, tags }) => {
const { allMarkdownRemark } = useStaticQuery(
graphql`
query {
allMarkdownRemark(
filter: { frontmatter: { pagetype: { eq: "blog" } } }
) {
edges {
node {
fields {
slug
}
id
frontmatter {
cate
hero
date(formatString: "YYYY.MM.DD")
title
tags
pagetype
}
}
}
}
}
`
)
let posts = allMarkdownRemark.edges.filter(post => {
if (post.node.fields.slug !== slug) {
// カテゴリの一致出力
if (post.node.frontmatter.cate === category) return true
// タグの一致出力。記事のタグの中に一致するものがあればtrueを返す。
for (const tag of tags) {
if (post.node.frontmatter.tags.includes(tag)) return true
}
}
})
if (!posts) return
if (posts.length > 5) {
function shuffle(list) {
var i = list.length
while (--i) {
var j = Math.floor(Math.random() * (i + 1))
if (i === j) continue
var k = list[i]
list[i] = list[j]
list[j] = k
}
return list
}
shuffle(posts)
posts = posts.slice(0, 6)
}
return (
<Related>
<h2>関連記事もあわせてお読みください</h2>
<RelatedList>
{posts.map((item, index) => {
return (
<article key={`relative${index}`}>
<Link className="p-entryCard__img" to={item.node.fields.slug}>
<Img
alt={item.node.frontmatter.title}
image={item.node.frontmatter.hero}
></Img>
<time dateTime={item.node.frontmatter.date}>
{item.node.frontmatter.date}
</time>
</Link>
<h3>
<Link to={item.node.fields.slug}>
{item.node.frontmatter.title}
</Link>
</h3>
</article>
)
})}
</RelatedList>
</Related>
)
}
export default Lists
const Related = styled.div`
max-width: 700px;
margin: 0 auto;
h2 {
text-align: center;
font-size: 2rem;
margin-bottom: 30px;
}
`
const RelatedList = styled.div`
article {
margin-bottom: 30px;
}
a {
color: var(--black);
text-decoration: none;
position: relative;
&:hover {
opacity: 0.3s;
}
time {
position: absolute;
background: rgba(255, 255, 255, 0.3);
left: 0;
top: 0;
line-height: 1;
padding: 5px 15px;
font-size: 1.4rem;
font-weight: 700;
}
}
h3 {
font-size: 1.7rem;
}
@media screen and (min-width: 768px) {
display: flex;
flex-wrap: wrap;
margin: 0 -15px;
article {
box-sizing: border-box;
width: 33.33%;
padding: 0 10px;
}
}
`
これで関連記事び一覧が出力できるようになりました!わーい。
読みこむたびに、ランダム表示されます。
まとめ・関連記事一覧を実装するとユーザーも嬉しい
記事詳細読了後、関連記事が表示されることで回遊率はグンと上がります。
実はリニューアル前も関連記事機能あったんですがロジックが甘くてほぼ機能していませんでした。
現在はGatsbyに切り替えてしっかり作り込んだおかげで、適切な関連記事が表示されるようになりました。
Googleアナリティクスで見ても、記事の検索機能もつけたせいかもしれませんが、平均ページ/セッション数が1.2ページからから1.83になり、150%伸びました。嬉しい限り。
ぜひ、Gatsbyで興味ある方は関連記事の実装チャレンジしてください。
次は「タグクラウド コンポーネントを作成する」です。
最後までお読みいただきありがとうございました。