webコーダーのsaco @sacocco_sacoya です。
前回に引き続きはじめてつくるNuxtサイト(三好アキさん著書)で学んだことを記事にします。
※ もし記述内容として誤りやアドバイスなどがありましたら、Xかお問合せフォームにてお知らせいただけるととても喜びます!
教材内の目次では第2章の「記事ページの作成」の記録です!
内容はNuxt.js3でブログの記事詳細ページを作成する、というものです。
生じた疑問点やエラー、教材と結果が違う箇所など、私が触ってみた結果も交えて記述していきたいと思います。
↓前回の記事はこちら
記事ページの作成 その1(概要)
前回はブログの記事一覧ページを作成し、各記事へのリンクを<NuxtLink>タグで作成するところまで勧めましたが、前回までの設定では各記事が存在していないため、記事ページを表示しようとすると404エラーページが表示されます。
データ出力用のマークダウンファイルだけが存在していて、記事そのものは存在していないということです。
作成したマークダウンファイルの情報を各ファイルに応じて出力するための、汎用ひな形ファイル(テンプレート)を作成していきます。
記事ページの作成 その2(ひな形ファイル作り)
まず、pagesフォルダ内にblogフォルダを作成します。
ここで作成したblogフォルダは、記事ページのひな形ファイルを保存するためのフォルダです。
復習:pagesフォルダについて
pagesフォルダ内に作成したvueファイルはページとして表示され、ファイル名がそのままURLとして使用されるのでした。
↓参照ブログ
汎用ファイルの作り方
このblogフォルダの中に、[id].vueというvueファイルを作成します。
Nuxtではファイル名を[]で囲むことで汎用ファイルであることを示します。
一般的には[id].vueや、[slug].vueなどがよく使われるようです。
まず作成した[id].vueが表示されるか確認するため、ファイル内に以下のコードを記述します。
<template>
<h1>記事ページです。</h1>
</template>
ブラウザをリロードして、仮にhttp://localhost:3000/blog/abcdというURLを入力します。
記事の内容ではなく、blog.vueの内容が表示されています。
この現象を修正するために、blog.vueをblogフォルダへ移動させます。
この状態のままだと一覧ページのURLが/blog/blogとなってしまうため、ファイル名をindex.vueへ変更します。
ここで再度http://localhost:3000/blog/abcdを開くと[id].vueの内容が表示されました。
/blog以降のURL名は123でもxyzでも表示されるため、[id].vueファイルが汎用的なテンプレートとして機能していることが確認できました。
これで各記事のデータを流し込むためのひな形ファイルが完成しました!
?:疑問点メモ
ここでなぜblog.vueをblogフォルダの中に配置し、ファイル名をindex.vueとすることで、アクセスするためのURLが http://〜/blogとなるのかが分からなかったため、chatGPTに尋ねました。
Nuxt.jsでは index.vueファイルがデフォルトで参照され、index.vueが配置されているフォルダ名がURLとしてマッピング(割当)される機能を持っているためということでした。
例として、/pages/works/index.vueというフォルダ構成となっている場合では、アクセスするためのURLが 〜/worksとなるということです。
記事ページの作成 その3(データを読み込む)
作成したひな形ファイルで、マークダウンファイルに記述したデータを読み込む設定を行っていきます。(この作業はblog.vueの作業と基本部分は同じです。)
作成した[id].vueファイルに以下のコードを記述しました。
<template>
<div>
<h1>記事ページです。</h1>
{{ data }}
</div>
</template>
<script setup>
const { data } = await useAsyncData("/blog/first-blog", () =>
queryContent("/blog/first-blog").findOne())
</script>
queryContent内の記述ですが、ここには取得したいデータのパスを記述します。
比較として、blog.vueでは以下のように指定していました。
<script setup>
const { data } = await useAsyncData("blogQuery", () =>
queryContent("/blog").sort({ id: -1 }).find())
</script>
blog.vueファイル内において、queryContentで取得する値を「/blog」というフォルダ名で指定しているのは、contentフォルダ>blogフォルダ内にあるマークダウンファイルのデータをすべて取得したいためでした。
一方、[id].vueでは記事データを1つ分だけ取得したいので、「/blog/first-blog」という風に、取得したいファイルを指定して、最後に.findOne()でデータを1つ分だけとして取得しています。
ここでブラウザをリロードすると以下のような画面が表示されます。
first-blog.mdの内容を取得することができました。
現在の指定のままでは、「first-blog.md」しか取得することができないため、各記事に応じて内容が変わるように設定を行っていきます。
useRoute().path
各記事に応じて内容が変わるように、Nuxtが用意してくれているuseRoute().pathを使用します。
useRoute().pathとはは、現在のURL(パス名)を取得してくれるもので、console.logでどのように取得されているか確認してみました。
<script setup>
const { data } = await useAsyncData("/blog/first-blog", () => queryContent("/blog/first-blog").findOne())
console.log(useRoute().path)//追記
</script>
現在のパス名が表示されています。
http://localhost:3000/blog/second-blog とパスを変更すると、blog/second-blogとして取得できています。
この機能を利用して、queryContent(取得したいデータのパス名を記述するためもの)の括弧内にuseRoute().pathを指定します。
<script setup>
const { data } = await useAsyncData("/blog/first-blog",
() => queryContent(useRoute().path).findOne())
</script>
ここでブラウザを確認すると、パスに応じて内容が変化するようになりました!
もう一点修正すべき点として、useAsyncDataの第一引数が「/blog/first-blog」となっているものを「useRoute().path」という風に変更します。
<script setup>
const { data } = await useAsyncData("useRoute().path",
() => queryContent(useRoute().path).findOne())
</script>
useAsyncDataの第一引数にはデータ読み取りの処理の名前として好きなものを命名出来ますが、他と重複しないものがよいということで教材ではuseRoute().path という風に指定されていました。
データをテンプレート内で取得・表示
取得した個別のブログ記事のデータを表示させるための記述をしていきます。
ここでの data の内容は、一覧ページで取得する際と違って配列ではないので、dataにそのまま . を繋げて取得することができます。
<template>
<div>
<div>
<nuxt-img :src="data.image" alt="blog-image" format="webp" />
</div>
<div>
<div>
<h1>{{ data.title }}</h1>
<p>{{ data.date }}</p>
<contentDoc />
</div>
</div>
</div>
</template>
<script setup>
const { data } = await useAsyncData("useRoute().path", () => queryContent(useRoute().path).findOne())
</script>
<contentDoc />
ここで<contentDoc />というタグが使用されていました。
<contentDoc />とは、NuxtContentが提供している機能で、URL(パス名)で指定された記事データの本文部分を自動で表示してくれます。
ブラウザを確認すると、以下のように表示されました。
パスに応じて、内容が変化しています!
一覧ページから各記事ページへアクセスしても同様に、該当ページのデータが表示されるようになりました!
これで詳細ページの完成です。
まとめ
- pagesフォルダ内にbolgフォルダを作成し、記事詳細ページのひな型を保存した
- Nuxtではファイル名を[]で囲むと汎用ファイルとして扱われる
- pagesフォルダ内に配置されたvueファイルは、ファイル名がURLパスに対応している
- pagesフォルダ内に「index.vue」としてファイル名を指定すると、そのファイルが配置されたフォルダ名がURLの一部となる
- 上記のことから、一覧ページとして作成したblog.vueをpagesフォルダ内のblogフォルダへ配置し、「index.vue」としてファイル名を変更することでパス名 〜/blogとして一覧ページへアクセスができるようになった
- queryContentにuseRoute().pathを指定することで、パスに応じて詳細記事ページの内容が変化する
- <contentDoc />はNuxtContentが提供している機能で、パス名で指定したデータの本文部分を自動で表示してくれる
つまづいた点、理解が難しかった点
contentフォルダ内と、pagesフォルダ内それぞれに「blog」というフォルダが存在していたため、少し混乱しました。
区別するために少し調べてみたところ、contentフォルダ内はマークダウンファイルやJSONファイルなどのコンテンツデータを配置するもの。
pagesフォルダに配置するファイルは、各データを流し込むためのテンプレートファイルを配置するためのもの。
状況によって異なってくることがありますが、今回の例だとこのような認識でよいかと思いました。
少しつまづきもありましたが、第2章を完走することができました。
次回はポートフォリオのTOPページ作成です!
本日は以上です。