[和訳] Nuxt3 公式サイト~Data Fetching
この記事について
この記事はNuxt3 公式サイト Data Fetching を和訳したものになります(日本語が不自然になってしまっている箇所があるのはごめんなさい)。
Data Fetching
Nuxt はアプリケーション内でデータの取得を処理するために useFetch
、useLazyFetch
、useAsyncData
、useAsyncLazyData
を提供します。
useFetch
ページ内、コンポーネント内、プラグイン内で、あらゆる URL から広くデータを取得するために useFetch
を使うことができます。
このコンポーザブルは useAsyncData
と $fetch
の便利なラッパーを提供します。URL とフェッチオプションに基づいてキーを自動的に生成し、サーバールートに基づいてリクエスト URL の型のヒントを提供し、API のレスポンス型を推論します。
詳細は下記ページを参照してください。
Example
<script setup>
const {data: count} = await useFetch('/api/count')
</script>
<template>
Page visits: {{ count }}
</template>
下記ページで実際の例を見たり編集したりできます。
useLazyFetch
このコンポーザブルは lazy:true
オプションが設定された useFetch
と同じように振る舞います。言い換えれば、非同期関数はナビゲーションをブロックしません。これはデータが null(またはカスタムのデフォルトファクトリで提供した値)である場合の処理をする必要があることを意味します。
詳細は下記ページを参照してください。
Example
<template>
<!-- 読み込み状態を処理する必要があります -->
<div v-if="pending">
Loading ...
</div>
<div v-else>
<div v-for="post in posts">
<!-- 何らかの処理 -->
</div>
</div>
</template>
<script setup>
const { pending, data: posts } = useLazyFetch('/api/posts')
watch(posts, (newPosts) => {
// posts は最初 null なのですぐにその中身のアクセスすることはできませんが、ウォッチすることはできます。
})
</script>
useAsyncData
ページ、コンポーネント、プラグインの中で、useAsyncData
を使用して非同期に解決されるデータにアクセスすることができます。
詳細は下記ページを参照してください。
Example
let counter = 0
export default () => {
counter++
return JSON.stringify(counter)
}
<script setup>
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
</script>
<template>
Page visits: {{ data }}
</template>
下記ページで実際の例を見たり編集したりできます。
useLazyAsyncData
このコンポーザブルは lazy:true
オプションが設定された useAsyncData
と同じように振る舞います。言い換えれば、非同期関数はナビゲーションをブロックしません。これはデータが null(またはカスタムのデフォルトファクトリで提供した値)である場合の処理をする必要があることを意味します。
詳細は下記ページを参照してください。
Example
<template>
<div>
{{ pending ? 'Loading' : count }}
</div>
</template>
<script setup>
const { pending, data: count } = useLazyAsyncData('count', () => $fetch('/api/count'))
watch(count, (newCount) => {
// count は最初 null なのですぐにその中身のアクセスすることはできませんが、ウォッチすることはできます。
})
</script>
Refreshing Data
ページを訪問する過程で、API から読み込んだデータを更新する必要がある場合があります。これはページ送り、結果のフィルタリング、検索などを選択した場合に発生する可能性があります。
useFetch()
コンポーザブルから返される refresh()
メソッドを利用して、異なるクエリパラメータでリフレッシュすることが可能です。
<script setup>
const page = ref(1);
const { data: users, pending, refresh, error } = await useFetch(() => `users?page=${page.value}&take=6`, { baseURL: config.API_BASE_URL }
);
function previous() {
page.value--;
refresh();
}
function next() {
page.value++;
refresh();
}
</script>
これを実現する鍵は、クエリパラメータが変わったときに useFetch()
コンポーザブルから返される refresh()
メソッドを呼ぶことです。
デフォルトでは、refresh()
は保留中のあらゆるリクエストをキャンセルし、その結果データや保留中の状態は更新されません。この新しいリクエストが解決されるまで、以前に待機していたプロミスは解決しません。この挙動は dedupe
オプションを設定することで防ぐことができます。dedupe
オプションは現在実行中のリクエストに対するプロミスがある場合に、代わりにそれを返します。
refresh({ dedupe: true })
refreshNuxtData
useAsyncData
、useLazyAsyncData
、useFetch
、useLazyFetch
のキャッシュを無効化し、リフェッチをトリガーします。
このメソッドは現在のページの全てのデータ取得をリフレッシュしたいときに便利です。
詳細は下記ページを参照してください。
Example
<template>
<div>
{{ pending ? 'Loading' : count }}
</div>
<button @click="refresh">Refresh</button>
</template>
<script setup>
const { pending, data: count } = useLazyAsyncData('count', () => $fetch('/api/count'))
const refresh = () => refreshNuxtData('count')
</script>
clearNuxtData
useAsyncData
と useFetch
のキャッシュされたデータ、エラーステータス、保留中のプロミスを削除します。
このメソッドは別ページのデータ取得を無効にしたい場合に便利です。
詳細は下記ページを参照してください。
Options API support
Nuxt3 は Options API 内で asyncData
を実行する方法を提供します。これを動作させるためには、defineNuxtComponent
でコンポーネント定義をラップする必要があります。
<script>
export default defineNuxtComponent({
fetchKey: 'hello',
async asyncData () {
return {
hello: await $fetch('/api/hello')
}
}
})
</script>
詳細は下記ページを参照してください。
fetch
and $fetch
Isomorphic ブラウザで fetch
を呼び出すと、cookie のようなユーザヘッダが直接 API へ送られます。しかしサーバサイドレンダリングでは、フェッチリクエストはサーバーの「内部」で行われるのでブラウザの cookie は含まれませんし、フェッチレスポンスから cookie が渡されるわけではありません。
詳細は下記ページを参照してください。
Example: Pass Client Headers to the API
useRequestHeaders
を使用し、サーバサイドから API にアクセス、cookie をプロキシすることができます。
下の例では、同型の $fetch
呼び出しにリクエストへッダを追加し、API エンドポイントがユーザによって最初に送信されたのと同じクッキーヘッダにアクセスできるようにしています。
<script setup>
const headers = useRequestHeaders(['cookie'])
const { data } = await useFetch('/api/me', { headers })
</script>
Example: Pass Cookies From Server-side API Calls on SSR Response
もし、内部リクエストからクライアントに戻って、クッキーを渡したりプロキシしたりしたい場合は自分で処理する必要があります。
export const fetchWithCookie = async (event: H3Event, url: string) => {
const res = await $fetch.raw(url)
const cookies = (res.headers.get('set-cookie') || '').split(',')
for (const cookie of cookies) {
appendHeader(event, 'set-cookie', cookie)
}
return res._data
}
<script setup lang="ts">
// このコンポーザブルは自動的にクライアントに cookie を渡します
const event = useRequestEvent()
const result = await fetchWithCookie(event, '/api/with-cookie')
onMounted(() => console.log(document.cookie))
</script>
Best Practice
これらのコンポーザブルによって返されるデータはページのペイロード内に保存されます。つまり、返されたキーのうち、コンポーネントで使用していないものは全てペイロードに追加されることになります。
api/mountains/everest
が次のようなオブジェクトを返すと想像してください。
{
"title": "Mount Everest",
"description": "Mount Everest is Earth's highest mountain above sea level, located in the Mahalangur Himal sub-range of the Himalayas. The China–Nepal border runs across its summit point",
"height": "8,848 m",
"countries": [
"China",
"Nepal"
],
"continent": "Asia",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Everest_kalapatthar.jpg/600px-Everest_kalapatthar.jpg"
}
もし、コンポーネントで title
と description
だけを使用するなら、$fetch
か pick
の結果を連鎖させてキーを選択することができます。
<script setup>
const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
</script>
<template>
<h1>{{ mountain.title }}</h1>
<p>{{ mountain.description }}</p>
</template>
Using Async Setup
async setup()
を使用している場合、最初の await
の後に現在のコンポーネントインスタンスは失われます(これは Vue3 の制限です)。useFetch
を複数回呼び出すなど、複数の非同期処理を使いたい場合は、<script setup>
を使うかセットアップの最後にまとめて await
する必要があります。
<script>
export default defineComponent({
async setup() {
const [{ data: organization }, { data: repos }] = await Promise.all([
useFetch(`https://api.github.com/orgs/nuxt`),
useFetch(`https://api.github.com/orgs/nuxt/repos`)
])
return {
organization,
repos
}
}
})
</script>
<template>
<header>
<h1>{{ organization.login }}</h1>
<p>{{ organization.description }}</p>
</header>
</template>
Direct Calling an API Endpoint
API を直接呼び出す必要がある場合もあります。Nuxt3 では Fetch API と同じ API でunjs/ofetch を使って(fetch
に加え)グローバルに使用できる $fetch
メソッドを提供します。
$fetch
を使用すると以下のようなメリットがあります。
サーバ上で動作している場合は、直接 API を呼びだし、クライアントサイドで動作している場合はクライアントサイドから API を呼び出す右ことを「スマート」に処理します(サードパーティの API を呼び出すことも可能です)。
加えて、レスポンスの自動解析やデータの文字列化などの便利な機能も含まれています。
Discussion