なぜブログを作ろうと思ったか
日々の思ったことを残すため
日々の生活で得る情報を、文章としてアウトプットするための場が欲しかったからです。インプットした情報を文章でアウトプットすると、頭の中が整理ができて、わかったこと・わからなかったことが明確になります。
日記や備忘が主な目的ですが、情報のアウトプットで少しでも誰かの役に立てたら嬉しいです。
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(ファイル冒頭の---で囲まれた部分。yaml形式)には、記事タイトル、日付、タグ、サムネイル画像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の一部になります。この記事だと/post/tech/create-myblogになります。
app
└──post
└── [category]
└── [slug]
└── page.tsxpage.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: カテゴリー3yamlファイルを扱うために、yaml-loaderをインストールします。
npm install yaml-loadernext.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
}カテゴリーおよびタグ別記事一覧
一覧ページです。カテゴリーに一致する記事、タグを含む記事を取得して一覧で表示します。
ページネーションは下記を参考にしました。
![[Next.js]App Router時代の静的サイトの作り方](https://res.cloudinary.com/zenn/image/upload/s--vhTXHPYx--/c_fit%2Cg_north_west%2Cl_text:notosansjp-medium.otf_55:%255BNext.js%255DApp%2520Router%25E6%2599%2582%25E4%25BB%25A3%25E3%2581%25AE%25E9%259D%2599%25E7%259A%2584%25E3%2582%25B5%25E3%2582%25A4%25E3%2583%2588%25E3%2581%25AE%25E4%25BD%259C%25E3%2582%258A%25E6%2596%25B9%2Cw_1010%2Cx_90%2Cy_100/g_south_west%2Cl_text:notosansjp-medium.otf_37:hiromu%2Cx_203%2Cy_121/g_south_west%2Ch_90%2Cl_fetch:aHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL3plbm4tdXNlci11cGxvYWQvYXZhdGFyL2VmYzI4ZWNmYTcuanBlZw==%2Cr_max%2Cw_90%2Cx_87%2Cy_95/v1627283836/default/og-base-w1200-v2.png?_a=BACAGSGT)
App Routerでのルーティングと画面の表示
当初はPage Routerで作っていましたが、App Routerで作り直しました。App Routerのほうがわかりやすくコンポーネントやデータフェッチを扱える印象です。Page Routerでは、GetStaticPropsで取得したデータをコンポーネントに渡してリレーしていたのですが、App Routerでそのあたりの無駄な処理を減らせました。
デザイン
お菓子のカスタードプリンが好きなので、Webサイトのデザインにしました🍮🍮🍮🍮🍮
CSSフレームワークは使っていません。CSSは勉強のためにチマチマと書きました。SVGファイルも作成したりフリー素材から拝借しました。楽しいです。
好き勝手に作っているので、良いデザインを学ばないといけないと痛感しています。
参考書籍・サイト
公式ドキュメントやさまざまなサイトを参考にしました。偉大な先人たちに感謝です。
ES2015(ES6)以降のJavaScriptの基本の勉強になった書籍です。いまいち自信が無かったため、基本から学び直しました。
https://www.amazon.co.jp/dp/B09BV2HGN3Next.jsのApp Routerの参考になりました。
![[Next.js]App Router時代の静的サイトの作り方](https://res.cloudinary.com/zenn/image/upload/s--vhTXHPYx--/c_fit%2Cg_north_west%2Cl_text:notosansjp-medium.otf_55:%255BNext.js%255DApp%2520Router%25E6%2599%2582%25E4%25BB%25A3%25E3%2581%25AE%25E9%259D%2599%25E7%259A%2584%25E3%2582%25B5%25E3%2582%25A4%25E3%2583%2588%25E3%2581%25AE%25E4%25BD%259C%25E3%2582%258A%25E6%2596%25B9%2Cw_1010%2Cx_90%2Cy_100/g_south_west%2Cl_text:notosansjp-medium.otf_37:hiromu%2Cx_203%2Cy_121/g_south_west%2Ch_90%2Cl_fetch:aHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL3plbm4tdXNlci11cGxvYWQvYXZhdGFyL2VmYzI4ZWNmYTcuanBlZw==%2Cr_max%2Cw_90%2Cx_87%2Cy_95/v1627283836/default/og-base-w1200-v2.png?_a=BACAGSGT)
おわりに
Reactを使ってみて、開発スピードの向上を感じました。コンポーネントを使いまわせるだけでもすごい便利です。Next.jsも、設定などが複雑ではなく、SSGなどを実現する機能もシンプルでわかりやすいため、開発しやすいと思いました。大きな労力を必要とせずにサクサク動くWebサイトを作成できて、楽しいです。
まだまだわかっていない部分ばかりですし、どんどんアップデートされていっていますので、勉強の余地しかないです。当ブログにも機能を追加していきたいです。