ページの追加、データの取得、エラーの処理
Nuxtでシンプルなページ、ネストされたページ、ダイナミックなページを追加することができます。Nuxtのコンポジション関数 useFetch を使ってデータをフェッチします。カスタマイズされた 404.vue と error.vue ファイルを使用して、エラーを処理します。
イントロダクション
Nuxt 3では、/pages/ ディレクトリをプロジェクトのルートに作成するだけで、Nuxtが自動的にアプリのすべてのルートを生成します。useFetch メソッドを使って、ページ上でデータを取得することも可能です。まずは、Nuxtアプリにページを追加する方法から見ていきましょう。
ページの追加
Nuxtでは、/pages/ ディレクトリにページを追加することで、簡単にルーティングを設定できます。
- シンプルなページ
Nuxtでは、次のように簡単なページを追加することができます。bashpages ├── index.vue ├── about.vue └── contact.vue
この構造は、Vueの標準的なアプリでも同じです。そのため、Nuxtアプリでは変更の必要はありません。
- シンプルなサブページ
Nuxtでは、以下のように簡単なサブページを追加することができます。bashpages ├── ... └── shop ├── index.vue ├── product-1.vue ├── product-2.vue └── ...
このように、Nuxt は /pages/ ディレクトリのサブディレクトリにインデックスファイルを必要とします。これにより、以下のルートを自動的に生成することができます。
[
{
name: 'shop',
path: '/shop',
component: '~/pages/shop/index.vue'
},
{
name: 'shop-product-1',
path: '/shop/product-1',
component: '~/pages/shop/product-1.vue'
},
{
name: 'shop-product-2',
path: '/shop/product-2',
component: '~/pages/shop/product-2.vue'
},
...
]
- ネストされたページ
Nuxtでは、以下のように簡単なネストページを追加することができます。
pages
├── ...
├── blog.vue
└── blog
├── index.vue
├── post-1.vue
├── post-2.vue
└── ...
- 動的なページと動的なネストされたページ
Nuxtでは、次のように角括弧を使った動的ページと動的ネストページを追加することができます。
pages
├── ...
├── blog.vue
├── blog
│ ├── index.vue
│ └── [slug].vue
└── shop
├── index.vue
└── [slug].vue
Nuxtアプリは、Vueの標準アプリと違いがないことが一目瞭然です。しかし、Nuxtアプリと標準的なVueアプリでは、ナビゲーションとルータビューのコンポーネントに若干の違いがあります。Vueの標準的なアプリでは、<router-link> と <router-view> コンポーネントは次のように使用されます。
<template>
<ul>
<li v-for="(item, index) in menu">
<router-link :to="item.path">{{ item.title }}</router-link>
</li>
</ul>
</template>
Nuxtでは、<NuxtLink> と <NuxtPage> コンポーネントは、Vueの標準アプリでは以下のように使用されます。
<template>
<ul>
<li v-for="(item, index) in menu">
<NuxtLink :to="item.path">{{ item.title }}</NuxtLink>
</li>
</ul>
<NuxtPage />
</template>
しかし、Nuxtアプリで <NuxtPage> コンポーネントを使用する場合には、気をつけなければならないことがあります。すべてのページでテンプレート内のシングルルート要素を使用しなければなりません。例えば、
<template>
<div>
<h2>{{ title }}</h2>
<ul>
<li v-for="(item, index) in menu">
<NuxtLink :to="item.path">{{ item.title }}</NuxtLink>
</li>
</ul>
</div>
</template>
上記の例は、 <h2> と <ul> 要素を1つの <div> 要素で囲んでいることに注目してください。標準的なVueアプリでは、以下のようにテンプレート内で複数のルート要素を使用することができます。
<template>
<h2>{{ title }}</h2>
<ul>
<li v-for="(item, index) in menu">
<NuxtLink :to="item.path">{{ item.title }}</NuxtLink>
</li>
</ul>
</template>
Vueの標準的なアプリでは、 <h2> と <ul> 要素を1つの <div> 要素で囲む必要はありません。ルート要素をいくつでも使うことができるのです。この方がすっきりしていると思うかもしれません。その場合でも、Vueの標準アプリと同じようにNuxtで <router-link> と <router-view> コンポーネントを使用することができます。これは、どちらも基本的にVue Routerを使用しているためです。ただし、予期せぬエラーや <suspense> コンポーネントを使用した際の警告を避けるため、NuxtアプリでもVue標準アプリでも、要素を1つの要素にラップすることが推奨されています。
useFetch を使ってデータを取得する
Nuxt 3には、非同期でデータを取得するための4つのComposable(合成関数)が付属しています。それらは useFetch、useLazyFetch、useAsyncData、useLazyAsyncData です。以下の手順でNuxtの useFetch 構成関数を使用することができます。
- プロジェクトルートに .env 環境ファイルを作成し、BASE_URL を http://localhost:3001 に設定して、以下のように設定する。.envbash
BASE_URL=http://localhost:3001
- Nuxt config ファイルで publicRuntimeConfig オプションを使用して、以下のように環境変数 BASE_URL をアプリの残りの部分に公開するランタイム設定を定義してください。nuxt.config.tsts
export default defineNuxtConfig({ publicRuntimeConfig: { BASE_URL: process.env.BASE_URL } });
- useFetch を使用してデータを取得します。
<script setup lang="ts"> const config = useRuntimeConfig(); const { data, error } = await useFetch('/api/data', { baseURL: config.BASE_URL }); </script>
非同期関数の結果は data 定数に渡され、useFetch 構成関数の場合は false boolean が error 定数に渡されることに注意してください。そのため、テンプレートで必要なデータのためにそれらを再構築する必要があります。 - テンプレートにデータを表示します。vue
<template> <div v-if="error">エラーが発生しました</div> <div v-else> <h1>{{ data.title }}</h1> <div v-html="data.contents"></div> </div> </template>
データを表示する前に、v-if条件を使って、errorリアクティブオブジェクトからエラーが発生しないことを確認できることに注目してください。
404.vueと error.vue でエラーを処理する
Nuxt 3は、SPAとSSRにおいて、404や500といったエラーを自動的に処理してくれます。エラーの表示にはNuxtのデフォルトのエラーページを利用することもできますが、以下の手順で簡単にカスタマイズすることができます。
- 以下のように、Nuxtプロジェクトに error.vue と 404.vue を追加します。bash
(root) └── error.vue └── pages └── 404.vue
- 以下の内容を /pages/ ディレクトリにある 404.vue ページに追加します。pages/404.vuevue
<template> <div> <h1>{{ title }}</h1> <p>{{ message }}</p> <p>{{ route.params }}</p> </div> </template> <script setup> import { useRoute } from 'vue-router'; const route = useRoute(); const title = 'Page Not Found'; const message = 'Sorry, bad luck!'; </script>
これで、例えば localhost:3001/xxx のような存在しないルートに対して、HTTP 404エラーが表示されるようになりました。GET http://localhost:3000/xxx 404 (Not Found)
また、カスタマイズした404ページが正しくレンダリングされ、画面上に以下のような結果が表示されるはずです。Page Not Found Sorry, bad luck! { "catchAll": [ "xxx" ] }
- ディレクトリルートにある error.vue ページに以下の内容を追加します。error.vuevue
<template> <div> <h1>{{ title }}</h1> <p>{{ error }}</p> </div> </template> <script setup> const error = useError(); const title = 'Error'; </script>
Nuxt 3には、オブジェクトに返されるグローバルエラーをキャッチするための、useError構成関数が付属しています。このオブジェクトでエラーの詳細にアクセスし、エラーメッセージをエラーページで表示することができます。そこで、以下のように contact ページに "TypeError" エラーを設定してみましょう。pages/contact.vuevue<script setup> const num = 123 num.toUpperCase() </script>
contactに移動すると、ブラウザのConsoleタブに以下のような警告が表示されます。
[Vue warn]: Unhandled error during execution of setup function
//...
Uncaught (in promise) TypeError: num.toUpperCase is not a function
//...
このページを更新すると、先の「TypeError」エラーに対して、ブラウザネットワーク上で次のようなHTTPエラーが発生することが予想されます。
GET <http://localhost:3001/contact> 500 (Internal Server Error)
そして、ブラウザの画面上の error.vue ページに、次のようなメッセージが表示されるはずです。
Error
{ "url": "/contact", "statusCode": "500", "statusMessage": "Internal Server Error", "message": "num.toUpperCase is not a function", ... , "data": "" }
Nuxtがエラーを処理してくれるので、app.vueでエラーを処理する必要がないことがおわかりいただけたと思います。
まとめ
Nuxt 3を使用すると、ページの追加、データの取得、エラー処理が簡単になります。次回は、Nuxtアプリでのレイアウト作成について詳しく見ていきます。