🍨

Nuxt3+microCMSでSSGしてJamstackサイトを作ろう(10/24更新)

2022/07/19に公開

表題の通り。Nuxt3のSSGがちゃんと動くようになったので国産のヘッドレスCMS・microCMSを使ってJamstackなブログやオウンドメディアを作ってみようや。

投稿した3ヶ月前ではFull Static Generateは出来なかったのですが、10月現在はできるようになりました! 当該部分を修正しています。

基本的にMicroCMSさん公式のブログに書いてあるチュートリアルに則り、追加としてSSGしてみただけの内容です。

事前準備・おことわり

  1. MicroCMS(ヘッドレスCMS)についてすでに知ってる前提で、BASE URL・API KEYを発行してることも前提とします
  2. Nuxt3は現在RC版です

Muxt3+MicroCMS+SSG

Nuxt3セットアップ

create-nuxt-appの質問攻めも無く5秒で終わります。デフォルトでTypeScriptも使う設定です。

$ npx nuxi init <project-name>
$ cd ./<project-name>
$ npm install

また、SASS(SCSS)を使ってる人は以下を。Nuxt2の地獄の依存性エラーは起きません

$ npm install sass sass-loader fibers

記事一覧ページを作る

まず記事一覧のページを作ります。Nuxt3ではv2のようにデフォルトで/pages/等のディレクトリは作られてないので自作しましょう。

/pages/index.vue
<template>
  <div>
    <h3>MicroCMS+Nuxt3 SSG</h3>
    <ol>
      <li v-for="article in data.contents" :key="article.id">
        <nuxt-link :to="`/${article.id}`">{{ article.title }}</nuxt-link>
      </li>
    </ol>
  </div>
</template>

<script setup>
const { data } = await useFetch("/<API名>", {
  baseURL: "https://<プロジェクト名>.microcms.io/api/v1",
  headers: {
    "X-MICROCMS-API-KEY": "<APIキー>",
  },
});
</script>

Nuxt3はVue3なのでComposition APIでの記述が推奨されています。
Nuxt v2ではasyncData()を使ってmicroCMSのAPIにアクセスしていましたが、Nuxt v3から追加されたuseFetch()を使ってアクセスします。詳しくはこちらの記事を参照(ありがとうございます)

記事詳細ページを作る

記事表示部を作ります。

/pages/[slug]/index.vue
<template>
  <main>
    <h1 style="margin-bottom: 20px">{{ article.title }}</h1>
    <p style="margin-bottom: 40px">
      <time :datetime="article.publishedAt" v-text="article.publishedAt" />
    </p>
    <div class="post" v-html="article.content" />
  </main>
</template>

<script setup>
const route = useRoute();
const slug = route.params.slug;
const { data: article } = await useFetch(`/<API名>/${slug}`, {
  baseURL: "https://<プロジェクト名>.microcms.io/api/v1",
  headers: {
    "X-MICROCMS-API-KEY": "<APIキー>",
  },
});
</script>

<style lang="scss" scoped>
* {
  margin: 0 auto;
  max-width: 800px;
}

:deep(.post) {
h2{font-size:24px;font-weight:700;margin:40px 0 1pc;border-bottom:1px solid #ddd}p,pre{line-height:1.8;letter-spacing:.2px}pre{background:#fafafa;border:1px solid #ddd;padding:1pc}
}
</style>

Nuxt 3では、動的ルートは_newsではなく[news]のように記述します。また、APIから帰ってくる定数の型定義も行っておくとより良いでしょう。
以下のように表示されればOK

API Keyを隠蔽する

CMSのコンテンツ表示の実装はできましたが、このままデプロイして公開するとAPIキーがもろバレして危ない。
本番環境で利用するには以下のように環境変数ファイルにAPI情報を格納して、PrivateとPublicで分けて情報を呼び出します。

  1. .envファイルにAPI情報を格納
.env
SERVICE_DOMAIN=***********
API_KEY=************************************
  1. 設定に追記
/nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'
const { API_KEY, SERVICE_DOMAIN } = process.env;

export default defineNuxtConfig({
    privateRuntimeConfig: {
        apiKey: API_KEY,
        serviceDomain: SERVICE_DOMAIN
    },
    publicRuntimeConfig: {
        apiKey: process.env.NODE_ENV !== 'production' ? API_KEY : undefined,
        serviceDomain: process.env.NODE_ENV !== 'production' ? SERVICE_DOMAIN : undefined,
    },
    link: [
        { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ],
})
  1. 各ファイルにてuseRuntimeConfig()で環境変数を呼び出す
各コンポーネント
<script setup>
const config = useRuntimeConfig();
const client = {
  serviceDomain: config.serviceDomain,
  apiKey: config.apiKey,
};

const { data } = await useFetch("/news", {
  baseURL: `https://${client.serviceDomain}.microcms.io/api/v1`,
  headers: {
    "X-MICROCMS-API-KEY": client.apiKey,
  },
});
</script>

※以前この記事を投稿した際は以下のようにSSGが出来なかったのですが、今は出来ました。

Nuxt3でfull static generateが出来ない!


Nuxt v2では途中からasyncDataを使い特定の設定をするとAPIのコールバックごと静的ファイルとして保存するfull static generateが使えたのですがNuxt3だと出来ない
公式Docsによると完全な静的生成は未実装らしい…もうすぐRCから正式に移行しそうですが大丈夫でしょうか。。。

SSG&PaaSデプロイ

あとはCSSで整えるなりして各自のPaaSにデプロイするだけです。設定は以下の通り。

コマンド nuxt generate
出力ディレクトリ .output/public
環境変数 .envファイルの情報

DevToolのネットワークタブを見れば分かる通り、事前に_payload.jsにAPIから取得した情報が入っており、実APIへのリクエストを行っていないことがわかります。

デプロイエラーが出るとき

おそらくNuxt3プロジェクトをCloudflare Pagesにてデプロイする場合以下のエラーが出る事があります。

Nuxt CLI v3.0.0-rc.5
ERROR Only file and data URLs are supported by the default ESM loader

Node.jsのバージョンが低すぎる事が原因ですので環境変数に以下のようにNODE_VERSIONとして指定しておきます。

最初、DevelopersIOさんの記事をもとに16.xx.xxで指定したのですがえらったので15.10.0で設定し直したら成功しました。

(備考)HTMLサニタイズ

v-htmlで取得されたHTMLをそのまま表示させると何かと危ないのでサニタイズしておくとよいでしょう。(SSGで静的ファイルにビルド時に生成するので特にやる必要も無いかもしれません)

$ npm install sanitize-html
$ npm install -D @types/sanitize-html

以下のように変更。

+ import sanitizeHtml from "sanitize-html";
// <iframe>が含まれるなら許容設定追加
+ sanitizeHtml.defaults.allowedTags = sanitizeHtml.defaults.allowedTags.concat("iframe");
+ sanitizeHtml.defaults.allowedAttributes["iframe"] = ["*"];
  <div class="post" v-html="article.content" />
+ <div class="post" v-html="sanitizeHtml(article.content)" />

その他わからないことがあれば公式ドキュメントを御覧下さい。
https://blog.microcms.io/nuxt3-create-blog/

Discussion