データの取得
Nuxt では、API からデータを取得の方法は 2 つあります。fetch または asyncData を使うことでできます。
Nuxt はコンポーネントの mounted()
フックでデータを取得するような、クライアントサイドアプリケーションにおける従来の Vue のデータの取得パターンをサポートしています。しかしユニバーサルアプリケーションでは、サーバーサイドレンダリング中にデータをレンダリングするために Nuxt 特有のフックを使う必要があります。これによりすべての必要なデータと一緒にページをレンダリングすることができます。
Nuxt は非同期なデータを読み込むために 2 つのフックを提供しています:
-
asyncData
。このフックは、 ページ コンポーネントのみで使用できます。fetch
とは異なり、クライアントサイドレンダリング中にローディングプレースホルダーを示しません: そのかわり、ルートナビゲーションが解決されるまでそれをブロックし、失敗するとエラーページを表示します。 -
fetch
(Nuxt 2.12 以降)。どのコンポーネントにでも配置することができ、(クライアントサイドレンダリング中の)読み込み状態やエラーをレンダリングするショートカットを提供します。
これらのフックは、選択した あらゆるデータ取得ライブラリ で使用することができます。HTTP API へリクエストを送るために @nuxt/http または @nuxt/axios を使用することをおすすめします。認証ヘッダーの設定のような、これらのライブラリのより詳しい情報はそれぞれのドキュメントで見つけることができるでしょう。
fetch
または asyncData
を定義し、それをコンポーネントやページでも定義するとミックスイン関数は呼び出されるかわりに上書きされます。fetch フック
fetch
フックは ページ コンポーネントに対してのみ使用でき、そしてコンポーネントへアクセスを持てないという違いがあります。もし fetch()
が context
オブジェクトを受け取っているなら、それは「レガシー」な fetch フックだと考えられます。この機能は非推奨なので、asyncData
または 無名ミドルウェア (anonymous middleware) に置き換えてください。fetch
は以下のように、サーバーサイドレンダリングではコンポーネントのインスタンスが作成されたとき、クライアントサイドでは遷移するときに呼び出されるフックです。fetch フックは解決される promise を(明示的に、または async/await
を使って暗黙的に)返却するべきです:
- サーバー上では、初期ページがレンダリングされる前
- クライアント上では、コンポーネントがマウントされた後
使い方
データの取得 (Fetching)
fetch フック内では、this
を介してコンポーネントインスタンスにアクセスできます。
data()
で宣言されていることを確認してください。宣言されている場合、取得したデータをこれらのプロパティに割り当てることができます。fetch 動作の変更
fetchOnServer
: Boolean
または Function
(デフォルト: true
)。サーバーがページをレンダリングする際に fetch()
を呼び出します。
fetchKey
: String
または Function
(デフォルトはコンポーネントのスコープ ID またはコンポーネント名)、コンポーネントの fetch 結果を識別するキー(または一意のキーを生成する関数)。(Nuxt 2.15 以降で有効、詳細情報の GitHub プルリクエスト )。
fetchDelay
: Integer
(デフォルト: 200
)。最小実行時間をミリ秒単位で設定します(過剰実行を防ぐため)。
fetchOnServer
が falsy(false
または false
になる得る値を返す)の場合、fetch
はクライアントサイドでのみ呼び出され、サーバーでコンポーネントをレンダリングする場合は、$fetchState.pending
は true
を返します。
export default {
data: () => ({
posts: []
}),
async fetch() {
this.posts = await this.$http.$get('https://api.nuxtjs.dev/posts')
},
fetchOnServer: false,
// 複数のコンポーネントは同じ `fetchKey` を返すことができ、Nuxt はそれらを両方を別々に追跡します
fetchKey: 'site-sidebar',
// 他の手段として、もっとコントロールしたい場合は、コンポーネントのインスタンスにアクセスできる関数を渡すこともできます
// これは `created` で呼び出され、フェッチされたデータに依存してはいけません
fetchKey(getCounter) {
// getCounterは、ユニークな fetchKey を生成する際に、シーケンス内の次の番号を
// 取得するために呼び出すことができるメソッドです
return this.someOtherData + getCounter('sidebar')
}
}
fetch 状態のアクセス
fetch
フックは、以下のプロパティを持つ this.$fetchState
をコンポーネントレベルで公開します:
-
pending
はfetch
がクライアントサイドで呼び出されたときにプレースホルダーを表示するかを表すBoolean
です -
error
はnull
もしくはfetch
フックで発生したError
です -
timestamp
は最後に fetch したタイムスタンプ、keep-alive
によるキャッシング で便利です
Nuxt が呼び出す fetch に加え、this.$fetch()
を使うことでコンポーネント内から手動(例として非同期データの再読み込み)で fetch を呼び出すことができます。
<template>
<p v-if="$fetchState.pending">Fetching mountains...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>
<h1>Nuxt Mountains</h1>
<ul>
<li v-for="mountain of mountains">{{ mountain.title }}</li>
</ul>
<button @click="$fetch">Refresh</button>
</div>
</template>
<script>
export default {
data() {
return {
mountains: []
}
},
async fetch() {
this.mountains = await fetch(
'https://api.nuxtjs.dev/mountains'
).then(res => res.json())
}
}
</script>
this.$nuxt.context
を使うことで、Nuxt context にアクセスできます。クエリ文字列の変化のリスニング
デフォルトでは、クエリ文字列の変化で fetch
フックは呼び出されません。クエリ文字列の変化を監視するには、ウォッチャに $route.query
を追加して $fetch
を呼び出します:
export default {
watch: {
'$route.query': '$fetch'
},
async fetch() {
// クエリ文字列の変化時にも呼び出される
}
}
キャッシング
<nuxt/>
や <nuxt-child/>
コンポーネントで keep-alive
ディレクティブを使うと、すでに訪れたページの fetch
呼び出しを保存することができます:
<template>
<nuxt keep-alive />
</template>
また、<nuxt>
コンポーネントへ keep-alive-props
プロパティを渡すことで、<keep-alive>
に渡す props を指定することもできます。
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />
ページコンポーネントを 10 ページ分だけメモリに保存します。
エラーハンドリング
fetch()
内で Nuxt redirect
または error
メソッドを使うべきではありません。代わりに、$fetchState.error
を使ったコンポーネント内でエラー処理する必要があります。データのフェッチングでエラーが発生した場合、$fetchState.error
でチェックし、エラーメッセージを表示します。
<template>
<div>
<p v-if="$fetchState.pending">Loading....</p>
<p v-else-if="$fetchState.error">Error while fetching mountains</p>
<ul v-else>
<li v-for="(mountain, index) in mountains" :key="index">
{{ mountain.title }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
mountains: []
}
},
async fetch() {
this.mountains = await fetch(
'https://api.nuxtjs.dev/mountains'
).then(res => res.json())
}
}
</script>
activated
フックを使う
最後に fetch
を呼び出したときのタイムスタンプを this.$fetchState.timestamp
から取得することができます(SSR も含む)。このプロパティを activated
フックと組み合わせることで、fetch
に 30 秒のキャッシュを追加することができます:
<template> ... </template>
<script>
export default {
data() {
return {
posts: []
}
},
activated() {
// 最後の fetch から30秒以上経っていれば、fetch を呼び出します
if (this.$fetchState.timestamp <= Date.now() - 30000) {
this.$fetch()
}
},
async fetch() {
this.posts = await fetch('https://api.nuxtjs.dev/posts').then(res =>
res.json()
)
}
}
</script>
最後の fetch
の呼び出しが 30 秒以内であれば、同じページへの遷移で fetch
は呼ばれません。
Async Data
asyncData
はユニバーサルなデータ取得のためのもう 1 つのフックです。非同期な状態を保存するために、コンポーネントのインスタンスにプロパティをセットする(または Vuex アクションをディスパッチする)必要がある fetch
とは異なり、asyncData
は単にその返却された値をコンポーネントのデータにマージします。以下は、@nuxt/http ライブラリを使った例です:
<template>
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.description }}</p>
</div>
</template>
<script>
export default {
async asyncData({ params, $http }) {
const post = await $http.$get(`https://api.nuxtjs.dev/posts/${params.id}`)
return { post }
}
}
</script>
fetch
と異なり、asyncData
フックから返却される promise は ルートの遷移の間 に解決されます。つまり、"loading placeholder" はクライアントサイドの遷移で表示されないということです(ただし読み込み中の状態をユーザーに示すために ローディングバー を使うことができます。Nuxt は代わりに asyncData
フックの終了を待ってから、次のページへ移動したりエラーページ を表示したりします)。
このフックはページレベルのコンポーネントのためだけに使うことができます。fetch
と異なり、asyncData はコンポーネントインスタンス (this
) にアクセスすることはできません。
そのかわりに、context を引数として受け取ります。asyncData
をデータの取得のために使うことができ、Nuxt は自動で返却されたオブジェクトをコンポーネントのデータと浅いマージ(shallow merge)します。
今後の例では、API からのデータの取得におすすめの @nuxt/http を使用します。
コンポーネントの非同期データ?
コンポーネントには asyncData
メソッドがないため、コンポーネント内でサーバーから非同期データを直接取得することはできません。この制限を回避するには、3 つの基本的なオプションがあります:
-
Nuxt 2.12 以降のバージョンで有効になった 新しい
fetch
フック を使う -
mounted
フックで API を呼び出し、ロード時にデータプロパティを設定します。欠点: サーバーサイドレンダリングでは機能しません。 -
ページコンポーネントの
asyncData
メソッドで API を呼び出し、データをプロパティとしてサブコンポーネントに渡します。サーバーのレンダリングは正常に機能します。欠点: ページのasyncData
は他のコンポーネントのデータを読み込むため読みにくい可能性があります。
クエリ文字列の変化のリスニング
デフォルトでは、クエリ文字列の変化で asyncData
メソッドは呼び出されません。ページネーションコンポーネントを作成するときなどにこの挙動を変えたい場合は、ページコンポーネントの watchQuery
プロパティを見るパラメータを設定することができます。