データの取得
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 プルリクエスト )。サーバーでレンダリングされたページをハイドレートする際、このキーはサーバーサイド fetch()
の結果をクライアントサイドのコンポーネントデータにマッピングするために使用されます。 詳細は PR を参照してください。
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
フックを使う
Nuxt は最後に fetch
を呼び出した時刻(SSR も含む)を this.$fetchState.timestamp
に直接格納します。このプロパティを 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
プロパティに設定することができます。












































