LOADING

saco blog logo

Webコーダー日記

Nuxt勉強【はじめてつくるNuxtサイト(三好アキさん著書) – 第2章 開発編】-その3

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ページ作成です!

本日は以上です。

PROFILE

saco profile img

SACO

1990年12月生まれ / 岡山県在住

WEBコーダーです。
2019年から独学でweb製作の勉強を開始し、2020年にコーダーに転身しました。
製作の備忘録など記していきます。
ショートカットキー、notionが好きです。

ポートフォリオサイトです

saco portfolio logo