Nuxt 2.12におけるfetchの仕組みを理解する
Nuxtは、バージョン2.12の最新リリースで新しいfetch
を導入しました。Fetchは、Nuxtアプリケーションにデータを取り込むための全く新しい方法を提供します。
この記事では、fetchフックのさまざまな機能を探り、その仕組みを理解していきます。
FetchフックとNuxtライフサイクル
Nuxtライフサイクルフックに関して、fetch
はcreated
フックの後のVueライフサイクル内に位置します。ご存知のように、すべてのVueライフサイクルフックは、それぞれのthis
コンテキストで呼び出されます。fetch
フックにも同じことが当てはまります。
Fetchフックは、サーバーサイドでコンポーネントインスタンスが作成された後に呼び出されます。そのため、fetch
内でthis
コンテキストが使用可能になります。
export default {
fetch() {
console.log(this)
}
}
これがページコンポーネントにとって何を意味するのか見てみましょう。
ページコンポーネント
this
コンテキストを使用することで、fetchはコンポーネントのデータを直接変更できます。つまり、Vuexストアのアクションをディスパッチしたり、ページコンポーネントからミューテーションをコミットしたりすることなく、コンポーネントのローカルデータを設定できます。
その結果、Vuexはオプションとなりますが、使用不可能になるわけではありません。必要に応じて、通常どおりthis.$store
を使用してVuexストアにアクセスできます。
fetchフックの可用性
fetch
を使用すると、**あらゆるVueコンポーネント**でデータを非同期にプリフェッチできます。つまり、/pages
ディレクトリにあるページコンポーネント以外に、/layouts
および/components
ディレクトリにある他のすべての.vue
コンポーネントもfetchフックの恩恵を受けることができます。
これがレイアウトコンポーネントとビルディングブロックコンポーネントにとって何を意味するのか見てみましょう。
レイアウトコンポーネント
新しいfetch
を使用することで、レイアウトコンポーネントから直接API呼び出しを行うことができます。これはv2.12のリリース以前は不可能でした。
使用例
- Nuxtレイアウトでバックエンドから設定データを取得して、フッターとナビゲーションバーを動的に生成する
- ナビゲーションバーでユーザー関連データ(例:ユーザープロフィール、ショッピングカートのアイテム数)を取得する
layouts/error.vue
でサイト関連データを取得する
ビルディングブロック(子/ネストされた)コンポーネント
子コンポーネントでもfetch
フックを使用できるようになったため、ページレベルのコンポーネントからデータフェッチタスクの一部をオフロードし、ネストされたコンポーネントに委任できます。これもv2.12のリリース以前は不可能でした。
これにより、ルートレベルのコンポーネントの責任が大幅に軽減されます。
**使用例 -** 子コンポーネントにpropsを渡すことはできますが、子コンポーネントに独自のデータフェッチロジックが必要な場合は、それが可能になりました!
複数のfetchフックの呼び出し順序
各コンポーネントが独自のデータフェッチロジックを持つことができるため、それぞれの呼び出し順序はどうなるのか疑問に思うかもしれません。
Fetchフックは、サーバーサイドで1回(Nuxtアプリへの最初のリクエスト時)、クライアントサイドでさらにルートに移動するときに呼び出されます。ただし、コンポーネントごとに1つのfetchフックを定義できるため、fetchフックは階層の順序で呼び出されます。
サーバーサイドでのfetchの無効化
さらに、必要に応じてサーバーサイドでfetchを無効にすることもできます。
export default {
fetchOnServer: false
}
こうすることで、fetchフックはクライアントサイドでのみ呼び出されます。fetchOnServer
がfalseに設定されている場合、コンポーネントがサーバーサイドでレンダリングされるときに$fetchState.pending
がtrue
になります。
エラー処理
新しいfetch
は、コンポーネントレベルでエラーを処理します。どのように処理されるかを見てみましょう。
データを非同期にフェッチしているため、新しいfetch()は、リクエストが完了して正常に処理されたかどうかを確認するための$fetchState
オブジェクトを提供します。
$fetchState
オブジェクトは次のようになります。
$fetchState = {
pending: true | false,
error: null | {},
timestamp: Integer
};
3つのキーがあります。
- **Pending -** クライアントサイドでfetchが呼び出されているときにプレースホルダーを表示できます
- **Error -** エラーメッセージを表示できます
- **Timestamp -** 最後のfetchのタイムスタンプが表示されます。これは
keep-alive
でのキャッシュに役立ちます
これらのキーは、コンポーネントのテンプレート領域で直接使用され、APIからデータをフェッチするプロセス中に関連するプレースホルダーを表示します。
<template>
<div>
<p v-if="$fetchState.pending">Fetching posts...</p>
<p v-else-if="$fetchState.error">Error while fetching posts</p>
<ul v-else>
…
</ul>
</div>
</template>
**コンポーネントレベル**でエラーが発生した場合、fetchフックでprocess.server
をチェックすることでサーバーサイドでHTTPステータスコードを設定し、throw new Error()
ステートメントでフォローアップできます。
async fetch() {
const post = await fetch(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
.then((res) => res.json())
if (post.id === this.$route.params.id) {
this.post = post
} else {
// set status code on server and
if (process.server) {
this.$nuxt.context.res.statusCode = 404
}
// use throw new Error()
throw new Error('Post not found')
}
}
この方法でHTTPステータスコードを設定すると、**SEOに役立ちます**。
メソッドとしてのFetch
新しいfetchフックは、ユーザーインタラクション時に呼び出すことができるメソッド、またはコンポーネントメソッドからプログラムで呼び出すことができるメソッドとしても機能します。
<!-- from template in template -->
<button @click="$fetch">Refresh Data</button>
// from component methods in script section
export default {
methods: {
refresh() {
this.$fetch()
}
}
}
Nuxtページのパフォーマンス向上
:keep-alive-props
propとactivated
フックを使用して、新しいfetchフックを使用してNuxtページコンポーネントのパフォーマンスを向上させることができます。
Nuxtでは、フェッチされたデータとともに**一定数のページをメモリにキャッシュ**できます。また、データを再フェッチする前に**数秒の遅延を追加**することもできます。
上記のいずれかの方法を機能させるには、汎用の<nuxt />
および<nuxt-child
>コンポーネントでkeep-alive
propを使用する必要があります。
<template>
<div>
<nuxt keep-alive />
</div>
</template>
さらに、:keep-alive-props
を<nuxt />
コンポーネントに渡して、フェッチされたデータとともに多数のページをキャッシュできます。
:keep-alive-props
propを使用すると、サイト内を移動している間にメモリに保持するページの最大数を指定できます。
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />
上記は、ページのパフォーマンスを向上させる1つの方法であり、より高レベルで汎用的です。次の方法は、$fetchState
のtimestamp
プロパティを使用して、データを再フェッチするまでの秒数の遅延と比較することで、fetchリクエスト呼び出しを最適化します。
ここでは、Vueのactivated
フックがNuxtのkeep-alive
propとともに使用され、データを再フェッチします。
export default {
activated() {
// Call fetch again if last fetch more than a minute ago
if (this.$fetchState.timestamp <= Date.now() - 60000) {
this.$fetch()
}
}
}
asyncDataとFetch
ページコンポーネントに関する限り、新しいfetch
はasyncData()
と非常によく似ています。どちらもローカルデータを扱います。ただし、以下に示すように、いくつかの重要な違いがあります。
Nuxt 2.12の時点では、asyncData
メソッドは引き続きアクティブな機能です。asyncData
と新しいfetch
の主な違いをいくつか見てみましょう。
asyncData
asyncData
はページレベルのコンポーネントのみに制限されますthis
コンテキストは使用できません- データを**返す**ことでペイロードを追加します
export default {
async asyncData(context) {
const data = await context.$axios.$get(
`https://jsonplaceholder.typicode.com/todos`
)
// `todos` does not have to be declared in data()
return { todos: data.Item }
// `todos` is merged with local data
}
}
新しいFetch
fetch
はすべてのVueコンポーネントで使用できますthis
コンテキストが使用可能です- 単にローカルデータを**変更**します
export default {
data() {
return {
todos: []
}
},
async fetch() {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/todos`
)
// `todos` has to be declared in data()
this.todos = data
}
}
Nuxt 2.12より前のFetch
しばらくNuxtを使用している場合は、以前のバージョンのfetch
が大きく異なっていたことがわかるでしょう。
これは破壊的な変更ですか?
いいえ、そうではありません。実際、既存のNuxtアプリケーションに破壊的な変更がないように、最初の引数として
context
を渡すことで、古いfetchを引き続き使用できます。
v2.12より**前**と**後**を比較したfetch
フックの注目すべき変更点のリストを以下に示します。
1. fetch
フックの呼び出し順序
**前 -** fetch
フックはコンポーネントの初期化前に呼び出されたため、fetchフック内でthis
は使用できませんでした。
**後 -** ルートにアクセスしたときに、サーバーサイドでコンポーネントインスタンスが作成された後にfetch
が呼び出されます。
2. this
とcontext
**前 -** context
が最初のパラメーターとして渡されることを前提として、ページレベルのコンポーネントでNuxt context
にアクセスできました。
export default {
fetch(context) {
// …
}
}
**後 -** パラメーターを渡さずに、Vueクライアントサイドフックと同様にthis
コンテキストにアクセスできます。
export default {
fetch() {
console.log(this)
}
}
3. fetch
フックの可用性
**前 -** サーバーサイドでデータを取得できるのは、ページ(ルートレベル)コンポーネントのみでした。
**後 -** あらゆるVueコンポーネントでデータを非同期にプリフェッチできるようになりました。
4. fetch
フックの呼び出し順序
**前 -** fetch
は、サーバーサイドで1回(Nuxtアプリへの最初のリクエスト時)、クライアントサイドでさらにルートに移動するときに呼び出すことができました。
**後 -** 新しいfetch
は古いfetchと同じですが…
…コンポーネントごとに1つのfetch
を持つことができるため、fetch
フックは階層の順序で呼び出されます。
5. エラー処理
**前 -** API呼び出し中にエラーが発生した場合にカスタムエラーページを表示するcontext.error
関数を使用しました。
**後 -** 新しいfetch
は、$fetchState
オブジェクトを使用して、API呼び出し中のテンプレート領域でエラーを処理します。
エラー処理はコンポーネントレベルで実行されます。
これは、Nuxt 2.12より前のようにユーザーにカスタムエラーページを表示できないという意味ですか?
表示できますが、ページレベルのコンポーネントデータの場合はasyncData()
を使用する場合のみです。fetch
を使用する場合は、this.$nuxt.error({ statusCode: 404, message: 'データが見つかりません' })
を使用してカスタムエラーページを表示できます。
結論
新しいfetchフックは多くの改善をもたらし、データのフェッチとルートレベルおよびビルディングブロックコンポーネントの編成に、全く新しい方法で柔軟性をもたらします!
同じルート内で複数のAPI呼び出しが必要な新しいNuxtプロジェクトを計画および設計する際には、必ず少し違った考え方が必要になります。
この記事が、新しいfetch
機能を理解するのに役立ったことを願っています。皆さんがそれで何を構築するのか、楽しみにしています。