なぜブログを作ろうと思ったか
日々の思ったことを残すため
日々の生活で得る情報を、文章としてアウトプットするための場が欲しかったからです。インプットした情報を文章でアウトプットすると、頭の中が整理ができて、わかったこと・わからなかったことが明確になります。
日記や備忘が主な目的ですが、情報のアウトプットで少しでも誰かの役に立てたら嬉しいです。
React や Next.js の勉強のため
スキルアップのためにブログを自作しました。仕事では Web 業界で流行の技術に触れる機会が無く、強く危機感を抱きました 😱 アウトプットだけなら既存のサービスを利用しても良いのですが、スキルを身に着ける点で自分でブログを作りたいと思いました。
React と Next.js を選択した理由は次の 3 点です。
- 流行しているから
- 無料で使えるから
- サクサク動くから
作ったもの(成果物)
当ブログです。
技術構成
Jamstack 構成です。DB、CSS フレームワーク、CMS などは使っていません。
React とは
Reactは、UI(ユーザーインターフェース)を構築するためのライブラリです。生みの親の Meta 社(旧 Facebook 社) はもちろん、多くの有名 IT 企業が React で開発をしています。JavaScript または TypeScript で記述できます。
今までの Web サイトは、画面を HTML と JavaScript を分けて記述して実装していたのですが、React は「HTML も JavaScript で書いたらいいのでは 🤔」と考えて生まれたそうです。
React は、機能ごとのパーツ=コンポーネントを作って組み合わせたり使いまわして画面を作るコンポーネント指向のライブラリです。コンポーネントを使って感じたメリットは次の 3 点です。
- 影響範囲が狭い
- 流用できる・拡張しやすい
- デザインの統一がしやすい
大きなプロジェクトほど、このメリットを強く実感できると思います。
Next.js とは
Next.js は、React をベースとしたフロントエンドフレームワークです。
多くの機能を提供してくれる素晴らしいフレームワークです。React 単体では手間のかかる部分の実装を簡単にしてくれます。
代表的な機能は、
- サーバーサイドでのページのレンダリング
- ディレクトリでのルーティング
- 最適化によるパフォーマンス向上
です。
サーバーサイドレンダリング(SSR)やスタティックサイト生成(SSG)によってページをサーバーサイドで事前にレンダリングすることで、初回のロード時間を短縮し、パフォーマンスを向上できます。サクサクです。
当ブログ作成前に、チュートリアルをやりました。このチュートリアルで作成したブログをもとに、機能の実装や CSS の追加などを施しながら当ブログを作成しました。
TypeScript とは
TypeScript は、JavaScript の拡張版です。構文は JavaScript と基本的に同じですが、型をつけられるため正確かつ安全なコードを書くことができます。
JavaScript は、変数の型を定義する必要がないため、想定していない型のデータを入れてエラーが出たり、エラーが出てから原因を調べなければならなかったりと、何かと非効率でした 😢
TypeScript は、この問題を解決するために、型をつけられます。型を見ればどんな値が入るのかわかりますし、事前にエラーを表示してくれます。おかげでコードの品質が上がり保守性も高まります。
引数にnumber
型、戻り値にstring
型を定義した例です。:
の後ろに型を記述します。引数がnumber
型ではない場合、エラーが出ます。
const isOddOrEven = (x: number): string => {
if (x % 2 === 0) {
return `${x}は偶数です`
} else {
return `${x}は奇数です`
}
}
console.log(isOddOrEven(1))
// OK! → "1は奇数です"
console.log(isOddOrEven('abc'))
// Error! → Argument of type 'string' is not assignable to parameter of type 'number'.
MDX とは
MDX は、Markdown の拡張版です。通常の Markdown と同じように文章を書ける上、JavaScript のコードや React コンポーネントを埋め込むこともできます。拡張子は.mdx
です。
Markdown の中で、import
を使ったりできます。当ブログでは、リンクカードなどの自作の React コンポーネント を MDX ファイル内で使用しています。下記は一例です。
import Button from 'button.js'
# 見出しです
本文です。
<Button>ボタンです</Button>
開発環境
機能の実装
開設時までに実装した機能は下記です。
- MDX 形式での記事の作成
- カテゴリーやタグでの記事分類
- カテゴリーおよびタグ別記事一覧
- ページネーション
- 記事の検索
MDX 形式での記事の作成
Next.js のチュートリアルでは Markdown を使用していますが、React コンポーネントを使いたいから MDX を採用しました。
MDX のを使うために next-mdx-remote を使用しています。記事の MDX ファイルは下記のように記述しています。Frontmatter には、記事タイトル、日付、タグ、サムネイル画像 URL を記述しています。本文は Markdown 形式で書いています。
---
title: 記事タイトル
date: '2024-01-01'
tags: [タグ1, タグ2, タグ3]
thumbnail: test.jpg
---
## 見出しだよ
記事本文だよ。
カテゴリーの記事分類
ブログサービスによくある、記事をカテゴリーで分けられる機能です。
カテゴリーごとにディレクトリを作成して記事を管理しています。
先人の知恵をお借りしたかったのですが、Next.js で実装している例がなかなか見つからず…。当ブログでの実装例を一部ですが紹介します。
ディレクトリ構成
記事のファイルを管理するためのディレクトリ _posts をプロジェクトの直下に作成します。
次に、_posts 下にカテゴリーのディレクトリを作ります。そして、その下に記事のファイルを作成します。
次のようなディレクトリ構成で記事を管理しています。
_posts
├── category1
│ ├── post1.mdx
│ ├── post2.mdx
│ └── post3.mdx
├── category2
│ ├── post1.mdx
│ ├── post2.mdx
│ └── post3.mdx
└── category3
├── post1.mdx
├── post2.mdx
└── post3.mdx
記事ページのディレクトリ構成は次のようになっています。_posts 下のディレクトリ名とファイル名は各記事にアクセスする際の URL の一部になります。例:https://example.com/post/category1/post1
app
└──post
└── [category]
└── [slug]
└── page.tsx
page.tsx 内のgenerateStaticParams()
で、カテゴリーと記事ファイル名を使ってルーティングを設定します。カテゴリーのディレクトリ名とファイル名を配列で渡します。
export async function generateStaticParams() {
const params = await getAllPostParams()
// [{ category: 'category1', slug: 'post1' },{ category: 'category1', slug: 'post2' },{ category: 'category1', slug: 'post3' },{ category: 'category2', slug: 'post1' },...]
return params.map((param) => ({ category: param.category, slug: param.slug }))
}
カテゴリー名の管理
カテゴリーは、ディレクトリ名と表示名を yaml ファイルで管理しています。プロジェクト直下に meta ディレクトリを作成し、その下に categories.yaml を作成します。
yaml ファイルは次のように記述します。slug
はディレクトリ名、name
はサイトでの表示名です。
categories:
- slug: category1
name: カテゴリー1
- slug: category2
name: カテゴリー2
- slug: category3
name: カテゴリー3
yaml ファイルを扱うために、yaml-loader をインストールします。
npm install yaml-loader
next.config.js ファイルに追記して使えるようにします。
module.exports = {
webpack: function (config) {
config.module.rules.push({
test: /\.ya?ml$/,
oneOf: [
{
resourceQuery: /stream/,
options: { asStream: true },
loader: 'yaml-loader',
},
{ loader: 'yaml-loader' },
],
})
return config
},
}
categories.ts を作成して、カテゴリーを取得する処理を作ります。記事の取得やルーティングのために使用します。なお、yaml ファイルに記載していないディレクトリは取得しません。
import categories from '@meta/categories.yaml'
type CategoryContent = {
slug: string
name: string
}
function generateCategoryMap(): { [key: string]: CategoryContent } {
const result: { [key: string]: CategoryContent } = {}
for (const category of categories.categories) {
result[category.slug] = category
}
return result
}
カテゴリーおよびタグ別記事一覧
一覧ページです。カテゴリーに一致する記事、タグを含む記事を取得して一覧で表示します。
ページネーションは下記を参考にしました。
App Router でのルーティングと画面の表示
当初は Page Router で作っていましたが、App Router で作り直しました。App Router のほうがわかりやすくコンポーネントやデータフェッチを扱える印象です。
App Router ではコンポーネントでデータフェッチ できるのが便利! Page Router では、GetStaticProps
で取得したデータをコンポーネントに渡してリレーしていましたのですが、App Router でそのあたりの無駄な処理を減らせました。
デザイン
お菓子のカスタードプリンが好きなので、Web サイトのデザインにしました 🍮🍮🍮🍮🍮
CSS フレームワークは使っていません。CSS は勉強のためにチマチマと書きました。SVG ファイルも作成したりフリー素材から拝借しました。楽しいです。
好き勝手に作っているので、良いデザインを学ばないといけないと痛感しています。
参考書籍・サイト
公式ドキュメントやさまざまなサイトを参考にしました。偉大な先人たちに感謝です。
ES2015(ES6)以降の JavaScript の基本の勉強になった書籍です。ES2015 にいまいち自信が無かったため、基本から学び直しました。
Next.js の App Router の参考になりました。
おわりに
React を使ってみて、開発スピードの向上を感じました。コンポーネントを使いまわせるだけでもすごい便利です。Next.js も、設定などが複雑ではなく、SSG などを実現する機能もシンプルでわかりやすいため、開発しやすいと思いました。大きな労力を必要とせずにサクサク動く Web サイトを作成できて、楽しいです。
まだまだわかっていない部分ばかりですし、どんどんアップデートされていっていますので、勉強の余地しかないです。当ブログにも機能を追加していきたいです。