Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

Nuxt.js と Sanity.io でブログを作り、Cloudflare Pages にデプロイする

最終更新 Markdown で表示Agent セットアップ

このチュートリアルでは、Nuxt.js と Sanity.io でブログアプリケーションを作り、Cloudflare Pages にデプロイします。Nuxt.js は、フロントエンドフレームワーク Vue.js 上に構築された静的サイトジェネレーターです。Sanity.io は、データベースを自前で維持せずにアプリケーションのデータを管理するヘッドレス CMS です。

前提条件

  • コンピューターに入っている、比較的新しい npm
  • Sanity.io アカウント

新しい Sanity プロジェクトを作成する

まず、Sanity のテンプレートのうちブログテンプレートを使い、新しい Sanity プロジェクトを作成します。設定を変えたい場合は、スキーマを変更するか、別のテンプレートを選べます。

Sanity をインストールし、データセットを設定する

npm から @sanity/cli クライアントをインストールし、ターミナルで sanity init を実行して、新しい Sanity プロジェクトを作成します。

npm i @sanity/cli
npx sanity init

Sanity プロジェクトを作成するとき、あらかじめ用意されたスキーマを使えます。スキーマは、Sanity データセット内のデータの形を表します。まったく新しいプロジェクトならスキーマをゼロから初期化することもありますが、ここでは Blog スキーマを選びます。

スキーマを確認する

プロジェクトを作成したら、フォルダーへ移動し、スタジオをローカルで起動できます。

cd my-sanity-project
npx sanity start

Sanity スタジオは、データセットへ新しいレコードを作成する場所です。デフォルトでは、スタジオをローカルで起動すると localhost:3333 で利用できます。そこにアクセスして、著者レコードを作成します。ブログ投稿もここで作成できます。

Sanity プロジェクトダッシュボードでのブログ投稿の作成

データセットをデプロイする

スタジオをデプロイする準備ができたら、sanity deploy を実行して、スタジオ用の一意の URL を選びます。これにより、自分(またはブログ管理に招待した人)が yoururl.sanity.studio ドメインでスタジオにアクセスできます。

npx sanity deploy

Sanity スタジオをデプロイしたら、次の手順を行います。

  1. Sanity の管理パネル(manage.sanity.io)を開きます。
  2. プロジェクトを探します。
  3. API を選びます。
  4. プロジェクトの許可された CORS オリジンに http://localhost:3000 を追加します。

これにより、Nuxt アプリケーションから Sanity データセットへのリクエストが許可リストに入ります。

Sanity プロジェクトの CORS 設定

新しい Nuxt.js プロジェクトを作成する

次に、Nuxt.js プロジェクトを作成します。新しいターミナルで、create-nuxt-app を使って新しい Nuxt プロジェクトをセットアップします。

npx create-nuxt-app blog

セットアップ中は、レンダリングモードに Universal (SSR / SSG)、デプロイ先に Static (Static/JAMStack hosting) を選んでください。

プロジェクトの作成が終わったら、新しいプロジェクトへ cd し、yarn dev(パッケージマネージャーに npm を選んだ場合は npm run dev)でローカル開発サーバーを起動します。

cd blog
npm run dev

Sanity.io を統合する

Nuxt.js アプリケーションのセットアップが終わったら、Sanity の @sanity/nuxt プラグインを Nuxt プロジェクトへ追加します。

npm i @nuxtjs/sanity @sanity/client

Nuxt.js アプリケーションでプラグインを設定するには、いくつかの設定情報が必要です。いちばん簡単な方法は、スタジオの sanity.json をアプリケーションディレクトリへコピーすることです(ほかの方法もあります。@nuxt/sanity のドキュメント を参照してください)。

sanity.json を追加するsh
cp ../my-sanity-project/sanity.json .

最後に、Nuxt 設定の build module として @nuxtjs/sanity を追加します。

nuxt.config.jsjs
{
	buildModules: ["@nuxtjs/sanity"];
}

コンポーネントをセットアップする

アプリケーションで Sanity を設定したら、ブログの描画に使い始められます。Sanity API からデータを取得して描画するページをいくつか用意します。Nuxt に慣れていない場合は、Nuxt ガイド を確認することをおすすめします。Nuxt でアプリケーションを作るときの基本が分かります。

インデックスページをセットアップする

まず、ルート(/)を訪れたときに描画される index ページを更新します。pages/index.vue です。

pages/index.vuehtml
<template>
	<div class="container">
		<div>
			<h1 class="title">My Blog</h1>
		</div>
		<div class="posts">
			<div v-for="post in posts" :key="post._id">
				<h2><a v-bind:href="post.slug.current" v-text="post.title" /></h2>
			</div>
		</div>
	</div>
</template>

<script>
	import { groq } from "@nuxtjs/sanity";

	export default {
		async asyncData({ $sanity }) {
			const query = groq`*[_type == "post"]`;
			const posts = await $sanity.fetch(query);
			return { posts };
		},
	};
</script>

<style>
	.container {
		margin: 2rem;
		min-height: 100vh;
	}
	.posts {
		margin: 2rem 0;
	}
</style>

Vue の SFC(シングルファイルコンポーネント)は、JavaScript、HTML、CSS を 1 つのファイルにまとめられる Vue 独自の機能です。pages/index.vue では、Vue コンポーネントを表す template タグを用意しています。

重要なのは、v-for ディレクティブで、posts 配列の各 post ごとに HTML を描画するよう Vue に指示している点です。

v-for ディレクティブを確認するhtml
<div v-for="post in posts" :key="post._id">
	<h2><a v-bind:href="post.slug.current" v-text="post.title" /></h2>
</div>

この posts 配列を埋めるために、Nuxt が提供する asyncData 関数を使います。ネットワークリクエストなどの非同期処理で、ページのデータを用意します。

$sanity オブジェクトは、Nuxt と Sanity.js の統合が提供します。Sanity データセットへリクエストするための手段です。$sanity.fetch を呼び出し、クエリを渡すと、データセットから特定のデータを取得し、ページのデータとして返せます。

Sanity を使ったことがない場合、Sanity がデータセット向けに提供する GROQ(GRaph Oriented Query language)は初めてかもしれません。GROQ は、データセットから欲しいデータを Sanity API に伝える強力な言語です。最初のクエリでは、_type の値が post であるオブジェクトをすべて取得するよう Sanity に指示します。

基本的な GROQ クエリjs
const query = groq`*[_type == "post"]`;
const posts = await $sanity.fetch(query);

ブログ投稿ページをセットアップする

index ページは、データセット内の各ブログ投稿へのリンクを、slug 値を URL にして描画します。たとえば、「Hello World」というブログ投稿を作り、スラッグを hello-world にした場合、Nuxt アプリケーションは /hello-world へのリクエストを扱い、対応するブログ投稿を Sanity から取得できる必要があります。

Nuxt は、pages_slug.vue 形式の新しいファイルを作ることで、こうしたページを標準でサポートします。ページの asyncData 関数では、params 引数でスラッグを参照できます。

pages/_slug.vuehtml
<script>
	export default {
		async asyncData({ params, $sanity }) {
			console.log(params); // { slug: "hello-world" }
		},
	};
</script>

これを踏まえ、pages/_slug.vue では、受け取った slug 値を使い、一致するブログ投稿を Sanity へ問い合わせ、その post のタイトルを描画します。

pages/_slug.vuehtml
<template>
	<div class="container">
		<div v-if="post">
			<h1 class="title" v-text="post.title" />
			<div class="content"></div>
		</div>
		<h4><a href="/">← Go back</a></h4>
	</div>
</template>

<script>
	import { groq } from "@nuxtjs/sanity";

	export default {
		async asyncData({ params, $sanity }) {
			const query = groq`*[_type == "post" && slug.current == "${params.slug}"][0]`;
			const post = await $sanity.fetch(query);
			return { post };
		},
	};
</script>

<style>
	.container {
		margin: 2rem;
		min-height: 100vh;
	}

	.content {
		margin: 2rem 0;
		max-width: 38rem;
	}

	p {
		margin: 1rem 0;
	}
</style>

たとえば /hello-world を訪れると、Nuxt は受け取ったスラッグ hello-world を使い、_typepost で、スラッグが /hello-world に一致するオブジェクトを Sanity へ GROQ クエリします。その集合から配列の先頭(JavaScript と同じ配列インデックス演算子 [0])を取り、ページデータに post として設定します。

ブログ投稿の本文を描画する

ブログの post タイトルは描画できていますが、ブログ投稿そのものの本文はまだありません。これを描画するには、sanity-blocks-vue-component パッケージをインポートします。Sanity の Portable Text 形式を Vue コンポーネントとして描画します。

まず、npm パッケージをインストールします。

npm i sanity-blocks-vue-component

パッケージをインストールしたら、plugins/sanity-blocks.js を作成します。コンポーネントをインポートし、Vue コンポーネント block-content として登録します。

plugins/sanity-blocks.jsjs
import Vue from "vue";
import BlockContent from "sanity-blocks-vue-component";
Vue.component("block-content", BlockContent);

Nuxt 設定の nuxt.config.js で、plugins ディレクティブの一部としてそのファイルをインポートします。

nuxt.config.jsjs
{
	plugins: ["@/plugins/sanity-blocks.js"];
}

pages/_slug.vue では、<block-content> コンポーネントで本文を描画できます。カスタム HTML コンポーネントの形式で、引数は 3 つです。:blocks は描画対象(ここでは child)、v-forchild の取得元(ここでは post.body)、:key は各投稿の一意な値(つまり _id)を渡して、Vue が 描画状態を追跡 しやすくします。

pages/_slug.vuehtml
<template>
	<div class="container">
		<div v-if="post">
			<h1 class="title" v-text="post.title" />
			<div class="content">
				<block-content
					:blocks="child"
					v-for="child in post.body"
					:key="child._id"
				/>
			</div>
		</div>
		<h4><a href="/">← Go back</a></h4>
	</div>
</template>

<script>
	import { groq } from "@nuxtjs/sanity";

	export default {
		async asyncData({ params, $sanity }) {
			const query = groq`*[_type == "post" && slug.current == "${params.slug}"][0]`;
			const post = await $sanity.fetch(query);
			return { post };
		},
	};
</script>

<style>
	.container {
		margin: 2rem;
		min-height: 100vh;
	}

	.content {
		margin: 2rem 0;
		max-width: 38rem;
	}

	p {
		margin: 1rem 0;
	}
</style>

pages/index.vue では、block-content コンポーネントで本文の要約を描けます。ブログ投稿本文の最初のブロックを取り出して描画します。

pages/index.vuehtml
<template>
	<div class="container">
		<div>
			<h1 class="title">My Blog</h1>
		</div>
		<div class="posts">
			<div v-for="post in posts" :key="post._id">
				<h2><a v-bind:href="post.slug.current" v-text="post.title" /></h2>
				<div class="summary">
					<block-content
						:blocks="post.body[0]"
						v-bind:key="post.body[0]._id"
						v-if="post.body.length"
					/>
				</div>
			</div>
		</div>
	</div>
</template>

<script>
	import { groq } from "@nuxtjs/sanity";

	export default {
		async asyncData({ $sanity }) {
			const query = groq`*[_type == "post"]`;
			const posts = await $sanity.fetch(query);
			return { posts };
		},
	};
</script>

<style>
	.container {
		margin: 2rem;
		min-height: 100vh;
	}
	.posts {
		margin: 2rem 0;
	}
	.summary {
		margin-top: 0.5rem;
	}
</style>

ブログスキーマには、プロジェクトへ追加できる項目がほかにもあります。練習として、ヘッドレス CMS での開発理解を深めるために、次のいずれかを試してください。

  • pages/authors.vue を作成し、著者の一覧を描画する(pages/index.vue と同様だが、_type == "author" のオブジェクト向け)
  • GROQ での参照の使い方 に関する Sanity のドキュメントを読み、ブログ投稿ページで著者情報を描画する

Cloudflare Pages で公開する

Cloudflare Pages での公開は 2 ステップです。まずプロジェクトを GitHub へプッシュし、次に Cloudflare Pages ダッシュボードで、その GitHub リポジトリを元に新しいプロジェクトをセットアップします。Pages は公開のたびにサイトの新しいバージョンをデプロイし、新しいプルリクエストを開くとプレビューデプロイも用意します。

プロジェクトを GitHub へプッシュするには、新しいリポジトリを作成 し、ローカルの Git リポジトリを GitHub へプッシュする手順に従います。

プロジェクトを GitHub へプッシュしたら、サイトを Pages にデプロイします。

  1. Cloudflare ダッシュボードで Workers & Pages ページを開きます。

    Workers & Pages を開く ↗
  2. Create application > Pages > Import an existing Git repository を選びます。

  3. 作成した新しい GitHub リポジトリを選び、Begin setup を選びます。

  4. Set up builds and deployments セクションの Build settings > Framework presetNuxt を選びます。Pages が正しいフィールドを自動で設定します。

サイトがデプロイされると、本番で確認するための一意の URL が届きます。

Sanity.io のデータが変わったときにプロジェクトを自動デプロイするには、Deploy Hooks を使えます。Pages project > Settings で新しい Deploy Hook URL を作成します。Sanity プロジェクトの Settings ページで Webhooks セクションを探し、次のように Deploy Hook URL を追加します。

Sanity ダッシュボードへの Deploy Hook URL の追加

これで、Sanity.io データセットを変更すると、Sanity が専用の Deploy Hook URL へリクエストし、新しい Cloudflare Pages のデプロイが始まります。新しいブログ投稿を追加したり、既存の投稿を編集したりしても、Pages アプリケーションを最新に保てます。

まとめ

このガイドを完了すると、Nuxt、Sanity.io、Cloudflare Pages で動くブログをデプロイできています。両方のコードベースのソースは GitHub にあります。

このチュートリアルが役に立った場合は、強力なサーバーレス関数プラットフォームである Cloudflare Workers で、既存サイトを拡張する方法にも興味があるかもしれません。詳細は Pages Functions でフロントエンド向け API を作る を参照してください。

役に立ちましたか?