mirror of
https://github.com/nuxt/nuxt.git
synced 2024-12-02 10:27:15 +00:00
151 lines
4.3 KiB
Markdown
151 lines
4.3 KiB
Markdown
# Data Fetching
|
||
|
||
Nuxt provides `useFetch` and `useAsyncData` to handle data fetching within your application.
|
||
|
||
## `useAsyncData`
|
||
|
||
Within your pages, components and plugins you can use `useAsyncData` to get access to data that resolves asynchronously.
|
||
|
||
### Usage
|
||
|
||
```js
|
||
useAsyncData(key: string, fn: () => Object, options?: { defer: boolean, server: boolean })
|
||
```
|
||
|
||
* **key**: a unique key to ensure that data fetching can be properly de-duplicated across requests
|
||
* **fn** an asynchronous function that returns a value.
|
||
* **options**:
|
||
* _defer_: whether to load the route before resolving the async function (defaults to `false`)
|
||
* _server_: whether the fetch the data on server-side (defaults to `true`)
|
||
* _transform_: A function that can be used to alter fn result after resolving
|
||
* _pick_: Only pick specified keys in this array from fn result
|
||
|
||
Under the hood, `defer: false` uses `<Suspense>` to block the loading of the route before the data has been fetched. Consider using `defer: true` and implementing a loading state instead for a snappier user experience.
|
||
|
||
### Example
|
||
|
||
```js [server/api/count.ts]
|
||
let counter = 0
|
||
export default () => {
|
||
counter++
|
||
return JSON.stringify(counter)
|
||
}
|
||
```
|
||
|
||
```vue [app.vue]
|
||
<script setup>
|
||
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
|
||
</script>
|
||
|
||
<template>
|
||
Page visits: {{ data }}
|
||
</template>
|
||
```
|
||
|
||
## `useFetch`
|
||
|
||
Within your pages, components and plugins you can use `useFetch` to get universally fetch from any URL.
|
||
|
||
This composable provides a convenient wrapper around `useAsyncData` and `$fetch` and automatically generates a key based on url and fetch options and infers API response type.
|
||
|
||
### Usage
|
||
|
||
```ts
|
||
useFetch(url: string, options?)
|
||
```
|
||
|
||
Available options:
|
||
|
||
* `key`: Provide a custom key
|
||
* Options from [ohmyfetch](https://github.com/unjs/ohmyfetch)
|
||
* `method`: Request method
|
||
* `params`: Query params
|
||
* `baseURL`: Base URL for request
|
||
* Options from `useAsyncData`
|
||
* `defer`
|
||
* `server`
|
||
* `pick`
|
||
* `transform`
|
||
|
||
### Example
|
||
|
||
```vue [app.vue]
|
||
<script setup>
|
||
const { data } = await useFetch('/api/count')
|
||
</script>
|
||
|
||
<template>
|
||
Page visits: {{ data.count }}
|
||
</template>
|
||
```
|
||
|
||
### Best practices
|
||
|
||
The data returned by `useAsyncData` will be stored inside the page payload. This means that every key returned that is not used in your component will be added to the payload.
|
||
|
||
::alert{icon=👉}
|
||
**We strongly recommend you only select the keys that you will use in your component.**
|
||
::
|
||
|
||
Imagine that `/api/mountains/everest` returns the following object:
|
||
|
||
```json
|
||
{
|
||
"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"
|
||
}
|
||
```
|
||
|
||
If you plan to only use `title` and `description` in your component, you can select the keys by chaining the result of `$fetch` or `pick` option:
|
||
|
||
```vue
|
||
<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
|
||
|
||
If you are using `async setup()`, the current component instance will be lost after the first `await`. (This is a Vue 3 limitation.) If you want to use multiple async operations, such as multiple calls to `useFetch`, you will need to use `<script setup>` or await them together at the end of setup.
|
||
|
||
::alert{icon=👉}
|
||
Using `<script setup>` is recommended, as it removes the limitation of using top-level await. [Read more](https://v3.vuejs.org/api/sfc-script-setup.html#top-level-await)
|
||
::
|
||
|
||
```vue
|
||
<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>
|
||
```
|