Nuxt3 + microCMSで簡単なブログサイトを作ってみる
この記事は「株式会社メンバーズ Jamstack研究会主催 Advent Calendar2023」の15日目の記事です。
はじめに
こんにちは、メンバーズの菅原です。
日頃Nuxt3を使って開発をしていますが、Nuxt3を静的サイトジェネレーターとして使ったことが無いので今回はmicroCMS の 公式のチュートリアルを参考にして microCMS と Nuxt3 を利用したJamstackなブログサイトを作成してみようと思います。
本記事では、microCMS と Nuxt3 で 記事一覧ページと記事詳細ページで構成されたシンプルなブログサイトを作り、SSG でVercel にデプロイしてページを表示するまでをご紹介いたします。
技術スタック
・microCMS
・Node.js v18.6.0
・Nuxt v3.8.2
・nuxt-microcms-module v3.0.2
Nuxtのプロジェクト作成
まずはNuxt側のセットアップを進めます。
$ npx nuxi@latest init microcms-nuxt
今回はパッケージマネージャーはyarnを選択しました。
セットアップが完了したらコンソールの指示に従ってディレクトリの移動をして開発サーバーを立ち上げてみます。
$ cd microcms-nuxt
$ yarn run dev
http://localhost:3000/ にアクセスしてNuxt3のWelcomeページが表示されたら完了です。
microCMSの準備
次に公式を参考にmicroCMS側のAPIを用意していきます。
テンプレートが用意されているので今回は「ブログ」を選択しました。
また、今回はテンプレートから生成されたAPIの他に「タグ」のAPIを追加で用意します。
エンドポイントはtags、形式は リスト形式を選択しました。
tagsのスキーマは以下の通りでカテゴリと同じ構成になります。
記事側にもtagsのフィールドを追加します。
カテゴリは記事に対して1つまでなのに対して、タグは記事に複数付けられるように種類は「複数コンテンツ参照」を選択します。
作成できたら、一覧画面のAPIプレビューボタンから、APIキーとAPIのサービスドメイン名を確認して、環境変数ファイル.envに記載しておきます。
MICROCMS_SERVICE_DOMAIN=サービスドメイン名 // .microcms.io は含まない
MICROCMS_API_KEY=APIキー
microCMSモジュールを導入
次に nuxt-microcms-moduleを導入します。
nuxt-microcms-module からはuseFetchがラップされた3つのcomposableが提供されます。
今回はこちらを使用してmicroCMSのコンテンツの取得をしていきます。
$ yarn add nuxt-microcms-module
次にnuxt.config.tsにモジュールを追加します。
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ["nuxt-microcms-module"],
microCMS: {
serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
apiKey: process.env.MICROCMS_API_KEY,
},
});
microCMSのコンテンツの型情報を作成
実装の前にmicroCMSのコンテンツの型を作っていきます。
// types/tag.ts
export type Tag = {
name?: string;
};
// types/category.ts
export type Category = {
name?: string;
};
// types/blog.ts
import type { MicroCMSImage, MicroCMSListContent } from "microcms-js-sdk";
import type { Category } from "./category";
import type { Tag } from "./tag";
export type Blog = {
title?: string;
content?: string;
eyecatch?: MicroCMSImage;
category: (MicroCMSListContent & Category) | null;
tags: Array<MicroCMSListContent & Tag> | null;
};
記事一覧の作成
最初にapp.vue を変更します。<NuxtPage/> コンポーネントを使用して pagesディレクトリへのルーティングを有効にします。
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
次にpages/index.vueを作成します。ここに記事の一覧を表示します。
先ほど導入したmicroCMSモジュールのuseMicroCMSGetListを使ってblogコンテンツの情報を取得し、その内容をページに表示します。
<template>
<ul>
<li v-for="blog in blogs?.contents" :key="blog.id">
<NuxtLink :to="`/${blog.id}`">
<img
:src="blog.eyecatch?.url"
:width="blog.eyecatch?.width"
:height="blog.eyecatch?.height"
/>
<div>
<div>
{{ blog.category?.name }}
</div>
<div>
{{ blog.tags?.map(({ name }) => name).join(" / ") }}
</div>
<div>
{{ blog.title }}
</div>
<div>
{{ blog.publishedAt ?? blog.createdAt }}
</div>
</div>
</NuxtLink>
</li>
</ul>
</template>
<script setup lang="ts">
import type { Blog } from "~~/types/blog";
const { data: blogs } = await useMicroCMSGetList<Blog>({
endpoint: "blogs",
});
</script>
以下のように表示されました。
記事詳細ページ作成
次に記事詳細ページを用意します。
pagesディレクトリにpages/[id].vueを作成します。(Nuxt3では[]で囲んだページファイルが動的ルーティングになります。)
useMicroCMSGetListDetailを使用してidに紐付く記事データを取得し表示します。idはuseRouteを使用すると現在のページの[id]部分を取得できるのでそちらを利用します。
<template>
<template v-if="blog">
<h1>
{{ blog.title }}
</h1>
<img
:src="blog.eyecatch?.url"
:width="blog.eyecatch?.width"
:height="blog.eyecatch?.height"
/>
<div>
<div>
{{ blog.category?.name }}
</div>
<div>
{{ blog.tags?.map(({ name }) => name).join(" / ") }}
</div>
<div>
{{ blog.publishedAt ?? blog.createdAt }}
</div>
</div>
<div v-html="blog.content"></div>
</template>
</template>
<script setup lang="ts">
import type { Blog } from "~~/types/blog";
const {
params: { id },
} = useRoute();
const { data: blog } = await useMicroCMSGetListDetail<Blog>({
endpoint: "blogs",
contentId: Array.isArray(id) ? id[0] : id,
});
</script>
以下のように記事が表示されました。
見た目を整える
最後に見た目を整えていきます。
Vuetifyの導入
今回はvueのUIライブラリのvuetify3を利用します。
vuetifyのインストールと設定を行っていきます。
アイコンとsassも使用したいので一緒にインストールします。
$ yarn add vuetify @mdi/font sass
plugins/vuetify.js を追加します。
// plugins/vuetify.js
import { createVuetify } from "vuetify";
import * as components from "vuetify/components";
export default defineNuxtPlugin((nuxtApp) => {
const vuetify = createVuetify({
ssr: true,
components,
});
nuxtApp.vueApp.use(vuetify);
});
nuxt.config.tsにビルドの設定をなどを追加します。
export default defineNuxtConfig({
devtools: { enabled: true },
build: {
transpile: ["vuetify"],
},
vite: {
ssr: {
noExternal: ["vuetify"],
},
},
css: ["vuetify/styles", "@mdi/font/css/materialdesignicons.css"],
modules: ["nuxt-microcms-module"],
microCMS: {
serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
apiKey: process.env.MICROCMS_API_KEY,
},
});
これでVuetifyの導入は完了です。
dayjsの導入
日付のフォーマットの変換を行いたいのでdayjsもインストールします。
$ yarn add dayjs
utils/dateFormat.tsに日時変換をする関数を定義します。
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);
export const dateFormat = (date: string, template: string = "YYYY/MM/DD") =>
dayjs.utc(date).tz("Asia/Tokyo").format(template);
これで事前の準備は完了です。
記事一覧ページ
それでは実際にページの見た目を整えていきます。
pages/index.vueのファイルを編集します。
<template>
<v-container>
<v-row>
<v-col v-for="blog in blogs?.contents" cols="4">
<v-card class="mx-auto" link :href="blog.id">
<v-img cover :src="blog.eyecatch?.url" alt="" />
<v-card-item>
<p class="text-h6 mb-2 font-weight-bold">{{ blog.title }}</p>
<v-chip
variant="outlined"
class="ma-1"
label
text-color="white"
color="primary"
size="small"
>
<v-icon start icon="mdi-format-align-left"></v-icon>
{{ blog.category?.name }}
</v-chip>
<v-chip
size="small"
v-for="tag in blog.tags"
prepend-icon="mdi-tag"
class="ma-1"
color="primary"
>
{{ tag.name }}
</v-chip>
<div class="d-flex flex-row-reverse">
<v-chip
prepend-icon="mdi-clock-time-four-outline"
class="mt-1"
variant="text"
>
{{ dateFormat(blog.publishedAt ?? blog.createdAt) }}
</v-chip>
</div>
</v-card-item>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
今回、記事一覧はカードデザインにしました。
記事詳細ページ
次は詳細ページです。
pages/[id].vueのファイルを編集します。
<template>
<template v-if="blogs">
<v-container>
<v-row justify="center">
<v-col cols="8">
<v-img :src="blogs.eyecatch?.url"></v-img>
<h1 class="text-h4 mt-6 mb-3 font-weight-bold">{{ blogs.title }}</h1>
<v-chip
variant="outlined"
class="ma-2"
label
text-color="white"
color="primary"
>
<v-icon start icon="mdi-format-align-left"></v-icon>
{{ blogs.category?.name }}
</v-chip>
<v-chip
v-for="tag in blogs.tags"
prepend-icon="mdi-tag"
class="ma-2"
color="primary"
>
{{ tag.name }}
</v-chip>
<p class="text-body text-end">
<v-icon start icon="mdi-clock-time-four-outline"></v-icon>
<span>{{ dateFormat(blogs.publishedAt ?? blogs.createdAt) }}</span>
</p>
<div class="blogContent mt-8" v-html="blogs.content"></div>
</v-col>
</v-row>
</v-container>
</template>
</template>
<style lang="scss">
.blogContent {
img {
width: 100%;
height: auto;
}
h2 {
font-size: 1.5rem;
margin: 4rem 0 1.5rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid #e0e0e0;
&:first-child {
margin-top: 0;
}
}
p {
margin: 1.5rem 0 0.5rem;
}
& > ol,
& > ul {
list-style-position: inside;
}
}
</style>
記事詳細は以下のようになりました。
デプロイ
これで一覧画面と詳細画面が完成したのでVercelにデプロイします。
先にここまでの作業内容をGitHubに上げます。
$ git commit -m "first commit"
$ git branch -M main
$ git remote add origin [リポジトリ]
$ git push -u origin main
次に、VercelにログインしてAdd New > Projectを選択して、GitHubのリポジトリを選択してビルドの設定を行います。
ビルドコマンドを yarn generate に設定しEnvironment Variablesに.envの内容をセットします。
Deployボタンを押してデプロイし、ページの表示が確認できたら完了です。
おわりに
今回は、MicroCMS + Nuxt3 でブログサイトを作りました。
簡単に短時間でサーバーレスで軽量なブログサイトを作ることができました。
microCMSは今回初めて触りましたが管理画面のUIもわかりやすくストレスなく開発を行うことができました。API経由でPOSTやDELETEなども可能なようなので次はCRUD操作を行うようなサイトを作ってみたいと思います。