🍨

Nuxt3+microCMSでSSGしてJamstackブログ作り(たかった)

2022/07/19に公開約4,700字

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

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

事前準備・おことわり

  1. MicroCMS(ヘッドレスCMS)についてすでに知ってる前提で、BASE URL・API KEYを発行してることも前提とします
  2. Vue3(Comoosition API)やTS等は知ってる前提
  3. 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

Nuxt3でfull static generateが出来ない!

CMSのコンテンツ表示の実装はできましたがこのままデプロイして公開するとAPIキーがもろバレして危ない。

Nuxt v2では途中からasyncDataを使い特定の設定をするとAPIのコールバックごと静的ファイルとして保存するfull static generateが使えたのですがNuxt3だと出来ない

公式Docsによると完全な静的生成は未実装らしい…もうすぐRCから正式に移行しそうですが大丈夫でしょうか。。。

https://v3.nuxtjs.org/guide/deploy/static-hosting/

こちら、もしご存じの方いらっしゃいましたらご教授願えますと幸いです。(PrivateRuntimeConfigを使えばAPIキーは隠蔽できるかもしれないです)

SSG&PaaSデプロイ

という状況なので気分はガタ落ちですが、一応最後にCloudflare Pagesにデプロイします。Netlifyと操作もほぼ同じだと思います。
ちゃんとデプロイできていればOKです。

エラーが出るときは

おそらくNuxt3プロジェクトをCF 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で設定し直したら成功しました。

追加設定

環境変数

上記で基本的な実装は終わりですが、本番環境で利用するには以下のような手順を踏んでおくとよりよいです。

  1. .envファイルにAPI情報を格納
.env
BASE_URL=https://************.microcms.io/api/v1
API_KEY=**********************************
  1. 設定に追記
/nuxt.config.ts
import { defineNuxtConfig } from "nuxt";
const { BASE_URL, API_KEY } = process.env;

export default defineNuxtConfig({
  publicRuntimeConfig: {
    baseURL: BASE_URL,
    apiKey: API_KEY,
  },
});
  1. 各ファイルにてuseRuntimeConfig()で環境変数を呼び出す
const ctx = useRuntimeConfig();
const { data } = await useFetch("/<API名>", {
  baseURL: ctx.baseURL,
  headers: {
    "X-MICROCMS-API-KEY": ctx.apiKey,
  },
});

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

ログインするとコメントできます