数据获取

Nuxt 提供了可组合项来处理应用内的数据获取。

Nuxt 提供了两个可组合项和一个内置库,用于在浏览器或服务器环境中执行数据获取:useFetchuseAsyncData$fetch

¥Nuxt comes with two composables and a built-in library to perform data-fetching in browser or server environments: useFetch, useAsyncData and $fetch.

简而言之:

¥In a nutshell:

  • $fetch 是发起网络请求最简单的方式。
  • useFetch$fetch 的封装器,在 通用渲染 中仅获取一次数据。
  • useAsyncDatauseFetch 类似,但提供更细粒度的控制。

useFetchuseAsyncData 共享一组通用的选项和模式,我们将在最后几节中详细介绍。

¥Both useFetch and useAsyncData share a common set of options and patterns that we will detail in the last sections.

useFetchuseAsyncData 的必要性

¥The need for useFetch and useAsyncData

Nuxt 是一个可以在服务器和客户端环境中运行同构(或通用)代码的框架。如果 $fetch 函数 用于在 Vue 组件的 setup 函数中执行数据获取,则可能会导致数据获取两次:一次在服务器端(用于渲染 HTML),另一次在客户端(当 HTML 被 hydrated 时)。这可能会导致混合问题,增加交互时间并导致不可预测的行为。

¥Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the $fetch function is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This can cause hydration issues, increase the time to interactivity and cause unpredictable behavior.

useFetchuseAsyncData 可组合项解决了这个问题,它确保如果在服务器上进行 API 调用,数据会通过有效负载转发到客户端。

¥The useFetch and useAsyncData composables solve this problem by ensuring that if an API call is made on the server, the data is forwarded to the client in the payload.

有效负载是一个可通过 useNuxtApp().payload 访问的 JavaScript 对象。它在客户端使用,以避免在浏览器 Hydration 期间 中执行代码时重新获取相同的数据。

¥The payload is a JavaScript object accessible through useNuxtApp().payload. It is used on the client to avoid refetching the same data when the code is executed in the browser during hydration.

使用 Nuxt DevToolsPayload tab 中检查此数据。
app.vue
<script setup lang="ts">
const { data } = await useFetch('/api/data')

async function handleFormSubmit() {
  const res = await $fetch('/api/submit', {
    method: 'POST',
    body: {
      // My form data
    }
  })
}
</script>

<template>
  <div v-if="data == null">
    No data
  </div>
  <div v-else>
    <form @submit="handleFormSubmit">
      <!-- form input tags -->
    </form>
  </div>
</template>

在上面的示例中,useFetch 将确保请求在服务器中发生并正确转发到浏览器。$fetch 没有这样的机制,当请求仅从浏览器发出时,$fetch 是更好的选择。

¥In the example above, useFetch would make sure that the request would occur in the server and is properly forwarded to the browser. $fetch has no such mechanism and is a better option to use when the request is solely made from the browser.

Suspense

Nuxt 在底层使用 Vue 的 <Suspense> 组件,以防止在所有异步数据可供视图使用之前进行导航。数据获取可组合项可以帮助你利用此功能,并根据每次调用选择最合适的方案。

¥Nuxt uses Vue's <Suspense> component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-call basis.

你可以添加 <NuxtLoadingIndicator> 以在页面导航之间添加进度条。

$fetch

Nuxt 包含 ofetch 库,并会以 $fetch 别名全局自动导入到你的应用中。

¥Nuxt includes the ofetch library, and is auto-imported as the $fetch alias globally across your application.

pages/todos.vue
<script setup lang="ts">
async function addTodo() {
  const todo = await $fetch('/api/todos', {
    method: 'POST',
    body: {
      // My todo data
    }
  })
}
</script>
请注意,仅使用 $fetch 无法提供 网络调用去重和导航预防。:br 建议使用 $fetch 进行客户端交互(基于事件),或在获取初始组件数据时与 useAsyncData 结合使用。¥Beware that using only $fetch will not provide network calls de-duplication and navigation prevention.
It is recommended to use $fetch for client-side interactions (event based) or combined with useAsyncData when fetching the initial component data.

:

Read more in Docs > API > Utils > Dollarfetch.

了解更多关于 $fetch 的信息。

¥Read more about $fetch.

::

将客户端标头传递给 API

¥Pass Client Headers to the API

在服务器上调用 useFetch 时,Nuxt 将使用 useRequestFetch 代理客户端标头和 Cookie(不打算转发的标头除外,例如 host)。

¥When calling useFetch on the server, Nuxt will use useRequestFetch to proxy client headers and cookies (with the exception of headers not meant to be forwarded, like host).

<script setup lang="ts">
const { data } = await useFetch('/api/echo');
</script>
// /api/echo.ts
export default defineEventHandler(event => parseCookies(event))

或者,以下示例展示了如何使用 useRequestHeaders 从服务器端请求(源自客户端)访问并向 API 发送 Cookie。使用同构 $fetch 调用,我们确保 API 端点能够访问用户浏览器最初发送的相同 cookie 标头。仅当你不使用 useFetch 时才需要这样做。

¥Alternatively, the example below shows how to use useRequestHeaders to access and send cookies to the API from a server-side request (originating on the client). Using an isomorphic $fetch call, we ensure that the API endpoint has access to the same cookie header originally sent by the user's browser. This is only necessary if you aren't using useFetch.

<script setup lang="ts">
const headers = useRequestHeaders(['cookie'])

async function getCurrentUser() {
  return await $fetch('/api/me', { headers })
}
</script>
你还可以使用 useRequestFetch 自动将标头代理到调用。
在将标头代理到外部 API 之前要非常小心,只包含你需要的标头。并非所有标头都可以安全地绕过,并且可能会引入不必要的行为。以下是一些不应被代理的常见标头列表:¥Be very careful before proxying headers to an external API and just include headers that you need. Not all headers are safe to be bypassed and might introduce unwanted behavior. Here is a list of common headers that are NOT to be proxied:
  • host, accept
  • content-length, content-md5, content-type
  • x-forwarded-host, x-forwarded-port, x-forwarded-proto
  • cf-connecting-ip, cf-ray

useFetch

useFetch 可组合项在 setup 函数中底层使用 $fetch 进行 SSR 安全的网络调用。

¥The useFetch composable uses $fetch under-the-hood to make SSR-safe network calls in the setup function.

app.vue
<script setup lang="ts">
const { data: count } = await useFetch('/api/count')
</script>

<template>
  <p>Page visits: {{ count }}</p>
</template>

此可组合项是对 useAsyncData 可组合项和 $fetch 实用程序的封装器。

¥This composable is a wrapper around the useAsyncData composable and $fetch utility.

Read more in Docs > API > Composables > Use Fetch.
Read and edit a live example in Docs > Examples > Features > Data Fetching.

useAsyncData

useAsyncData 可组合项负责封装异步逻辑并在解析后返回结果。

¥The useAsyncData composable is responsible for wrapping async logic and returning the result once it is resolved.

useFetch(url) 几乎等同于 useAsyncData(url, () => event.$fetch(url))。:br 它是针对最常见用例的开发者体验糖。(你可以在 useRequestFetch 上了解更多关于 event.fetch 的信息。)

在某些情况下,使用 useFetch 可组合组件并不合适,例如当 CMS 或第三方提供自己的查询层时。在这种情况下,你可以使用 useAsyncData 封装你的调用,同时仍然保留可组合组件提供的优势。

¥There are some cases when using the useFetch composable is not appropriate, for example when a CMS or a third-party provide their own query layer. In this case, you can use useAsyncData to wrap your calls and still keep the benefits provided by the composable.

pages/users.vue
<script setup lang="ts">
const { data, error } = await useAsyncData('users', () => myGetFunction('users'))

// This is also possible:
const { data, error } = await useAsyncData(() => myGetFunction('users'))
</script>
useAsyncData 的第一个参数是一个唯一键,用于缓存第二个参数(查询函数)的响应。可以通过直接传递查询函数来忽略此键,该键将自动生成。

由于自动生成的键仅考虑调用 useAsyncData 的文件和行,因此建议始终创建自己的键以避免出现不必要的行为,例如在创建自定义可组合组件封装 useAsyncData 时。:br
设置键对于在使用 useNuxtDatarefresh specific data 的组件之间共享相同数据非常有用。
pages/users/[id].vue
<script setup lang="ts">
const { id } = useRoute().params

const { data, error } = await useAsyncData(`user:${id}`, () => {
  return myGetFunction('users', { id })
})
</script>

useAsyncData 可组合函数是一种封装并等待多个 $fetch 请求完成,然后处理结果的绝佳方式。

¥The useAsyncData composable is a great way to wrap and wait for multiple $fetch requests to be completed, and then process the results.

<script setup lang="ts">
const { data: discounts, status } = await useAsyncData('cart-discount', async () => {
  const [coupons, offers] = await Promise.all([
    $fetch('/cart/coupons'),
    $fetch('/cart/offers')
  ])

  return { coupons, offers }
})
// discounts.value.coupons
// discounts.value.offers
</script>
useAsyncData 用于获取和缓存数据,不会触发副作用(例如调用 Pinia 操作),因为这可能会导致意外行为,例如重复执行并返回空值。如果你需要触发副作用,请使用 callOnce 实用程序。```vue
::

:

:read-more{to="/docs/api/composables/use-async-data"}

了解更多关于 `useAsyncData` 的信息。

¥Read more about `useAsyncData`.

::

<a id="return-values"></a>

## 返回值

¥Return Values

`useFetch` 和 `useAsyncData` 具有如下所示的相同返回值。

¥`useFetch` and `useAsyncData` have the same return values listed below.

* `data`:传入的异步函数的结果。

* `refresh`/`execute`:一个函数,用于刷新 `handler` 函数返回的数据。

* `clear`:一个函数,用于将 `data` 设置为 `undefined`,将 `error` 设置为 `null`,将 `status` 设置为 `idle`,并将当前待处理的请求标记为已取消。

* `error`:如果数据获取失败,则返回一个错误对象。

* `status`:一个字符串,指示数据请求的状态(`"idle"`、`"pending"`、`"success"`、`"error"`)。


::note
`data`、`error` 和 `status` 是 Vue 引用,在 `<script setup>` 中使用时可通过 `.value` 访问。
::

默认情况下,Nuxt 会等待 `refresh` 完成后才能再次执行。

¥By default, Nuxt waits until a `refresh` is finished before it can be executed again.


::note
如果你尚未在服务器上获取数据(例如,使用 `server: false`),则在 hydration 完成之前,数据将不会被获取。这意味着,即使你在客户端等待 `useFetch`,`data` 在 `<script setup>` 中仍将保持为 null。
::

<a id="options"></a>

## 选项

¥Options

[`useAsyncData`](/docs/api/composables/use-async-data) 和 [`useFetch`](/docs/api/composables/use-fetch) 返回相同的对象类型,并接受一组通用的选项作为它们的最后一个参数。它们可以帮助你控制可组合项的行为,例如导航阻止、缓存或执行。

¥[`useAsyncData`](/docs/api/composables/use-async-data) and [`useFetch`](/docs/api/composables/use-fetch) return the same object type and accept a common set of options as their last argument. They can help you control the composables behavior, such as navigation blocking, caching or execution.

<a id="lazy"></a>

### 延迟加载

¥Lazy

默认情况下,数据获取可组合项将等待其异步函数解析完毕后,再使用 Vue 的 Suspense 导航到新页面。在使用 `lazy` 选项进行客户端导航时,可以忽略此功能。在这种情况下,你必须使用 `status` 值手动处理加载状态。

¥By default, data fetching composables will wait for the resolution of their asynchronous function before navigating to a new page by using Vue's Suspense. This feature can be ignored on client-side navigation with the `lazy` option. In that case, you will have to manually handle loading state using the `status` value.

```vue twoslash [app.vue]
<script setup lang="ts">
const { status, data: posts } = useFetch('/api/posts', {
  lazy: true
})
</script>

<template>
  <!-- you will need to handle a loading state -->
  <div v-if="status === 'pending'">
    Loading ...
  </div>
  <div v-else>
    <div v-for="post in posts">
      <!-- do something -->
    </div>
  </div>
</template>
你也可以使用 useLazyFetchuseLazyAsyncData 作为便捷的方法来执行相同的操作。¥You can alternatively use useLazyFetch and useLazyAsyncData as convenient methods to perform the same.
<script setup lang="ts">
const { status, data: posts } = useLazyFetch('/api/posts')
</script>
:
Read more in Docs > API > Composables > Use Lazy Fetch.
了解更多关于 useLazyFetch 的信息。¥Read more about useLazyFetch.:::
Read more in Docs > API > Composables > Use Lazy Async Data.
了解更多关于 useLazyAsyncData 的信息。¥Read more about useLazyAsyncData.::

仅客户端抓取

¥Client-only fetching默认情况下,数据获取可组合项将在客户端和服务器环境中执行其异步函数。将 server 选项设置为 false,以便仅在客户端执行调用。初始加载时,数据在 hydration 完成之前不会被获取,因此你必须处理待处理状态。不过,在后续的客户端导航中,数据将被等待加载后再加载页面。¥By default, data fetching composables will perform their asynchronous function on both client and server environments. Set the server option to false to only perform the call on the client-side. On initial load, the data will not be fetched before hydration is complete so you have to handle a pending state, though on subsequent client-side navigation the data will be awaited before loading the page.与 lazy 选项结合使用,这对于首次渲染时不需要的数据(例如,非 SEO 敏感数据)非常有用。¥Combined with the lazy option, this can be useful for data that is not needed on the first render (for example, non-SEO sensitive data).
/* This call is performed before hydration */
const articles = await useFetch('/api/article')

/* This call will only be performed on the client */
const { status, data: comments } = useFetch('/api/comments', {
  lazy: true,
  server: false
})
useFetch 可组合项应在设置方法中调用,或在生命周期钩子函数的顶层直接调用,否则应使用 $fetch 方法。¥The useFetch composable is meant to be invoked in setup method or called directly at the top level of a function in lifecycle hooks, otherwise you should use $fetch method.

最小化负载大小

¥Minimize payload sizepick 选项可帮助你通过仅选择希望从可组合项返回的字段来最小化存储在 HTML 文档中的有效负载大小。¥The pick option helps you to minimize the payload size stored in your HTML document by only selecting the fields that you want returned from the composables.
<script setup lang="ts">
/* only pick the fields used in your template */
const { data: mountain } = await useFetch('/api/mountains/everest', {
  pick: ['title', 'description']
})
</script>

<template>
  <h1>{{ mountain.title }}</h1>
  <p>{{ mountain.description }}</p>
</template>
如果你需要更多控制或映射多个对象,你可以使用 transform 函数来更改查询结果。¥If you need more control or map over several objects, you can use the transform function to alter the result of the query.
const { data: mountains } = await useFetch('/api/mountains', {
  transform: (mountains) => {
    return mountains.map(mountain => ({ title: mountain.title, description: mountain.description }))
  }
})
picktransform 均不会阻止初始获取不需要的数据。但它们会阻止将不需要的数据添加到从服务器传输到客户端的有效负载中。

缓存和重新获取

¥Caching and refetching

¥KeysuseFetchuseAsyncData 使用键来防止重新获取相同的数据。¥useFetch and useAsyncData use keys to prevent refetching the same data.
  • useFetch 使用提供的 URL 作为键。或者,也可以将 key 值作为最后一个参数传入 options 对象。
  • 如果 useAsyncData 的第一个参数是字符串,则将其用作键。如果第一个参数是执行查询的处理函数,则会为你生成一个与 useAsyncData 实例的文件名和行号唯一的键。
要通过键获取缓存数据,你可以使用 useNuxtData

共享状态和选项一致性

¥Shared State and Option Consistency当多个组件使用与 useAsyncDatauseFetch 相同的键时,它们将共享相同的 dataerrorstatus 引用。这确保了跨组件的一致性,但需要一些选项保持一致。¥When multiple components use the same key with useAsyncData or useFetch, they will share the same data, error and status refs. This ensures consistency across components but requires some options to be consistent.以下选项在使用相同键的所有调用中必须保持一致:¥The following options must be consistent across all calls with the same key:
  • handler 函数
  • deep 选项
  • transform 函数
  • pick 数组
  • getCachedData 函数
  • default
// ❌ This will trigger a development warning
const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false })
const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
以下选项可以安全地不同而不会触发警告:¥The following options can safely differ without triggering warnings:
  • server
  • lazy
  • immediate
  • dedupe
  • watch
// ✅ This is allowed
const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { immediate: true })
const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { immediate: false })
如果你需要独立的实例,请使用不同的键:¥If you need independent instances, use different keys:
// These are completely independent instances
const { data: users1 } = useAsyncData('users-1', () => $fetch('/api/users'))
const { data: users2 } = useAsyncData('users-2', () => $fetch('/api/users'))

响应式键

¥Reactive Keys你可以使用计算引用、普通引用或 getter 函数作为键,从而实现动态数据获取,并在依赖发生变化时自动更新:¥You can use computed refs, plain refs or getter functions as keys, allowing for dynamic data fetching that automatically updates when dependencies change:
// Using a computed property as a key
const userId = ref('123')
const { data: user } = useAsyncData(
  computed(() => `user-${userId.value}`),
  () => fetchUser(userId.value)
)

// When userId changes, the data will be automatically refetched
// and the old data will be cleaned up if no other components use it
userId.value = '456'

刷新并执行

¥Refresh and execute如果你想手动获取或刷新数据,请使用可组合项提供的 executerefresh 函数。¥If you want to fetch or refresh data manually, use the execute or refresh function provided by the composables.
<script setup lang="ts">
const { data, error, execute, refresh } = await useFetch('/api/users')
</script>

<template>
  <div>
    <p>{{ data }}</p>
    <button @click="() => refresh()">Refresh data</button>
  </div>
</template>
execute 函数是 refresh 的别名,其工作方式完全相同,但在获取 并非立即发布 时更具语义。¥The execute function is an alias for refresh that works in exactly the same way but is more semantic for cases when the fetch is not immediate.
要全局重新获取或使缓存数据无效,请参阅 clearNuxtDatarefreshNuxtData

清除

¥Clear如果你出于某种原因想要清除提供的数据,而不需要知道传递给 clearNuxtData 的具体键,你可以使用可组合项提供的 clear 函数。¥If you want to clear the data provided, for whatever reason, without needing to know the specific key to pass to clearNuxtData, you can use the clear function provided by the composables.
<script setup lang="ts">
const { data, clear } = await useFetch('/api/users')

const route = useRoute()
watch(() => route.path, (path) => {
  if (path === '/') clear()
})
</script>

观看

¥Watch要在应用中的其他响应式值每次发生变化时重新运行获取函数,请使用 watch 选项。你可以将其用于一个或多个可监视元素。¥To re-run your fetching function each time other reactive values in your application change, use the watch option. You can use it for one or multiple watchable elements.
<script setup lang="ts">
const id = ref(1)

const { data, error, refresh } = await useFetch('/api/users', {
  /* Changing the id will trigger a refetch */
  watch: [id]
})
</script>
请注意,监视响应式值不会更改获取的 URL。例如,由于 URL 是在调用函数时构建的,因此这将持续获取相同的用户初始 ID。¥Note that watching a reactive value won't change the URL fetched. For example, this will keep fetching the same initial ID of the user because the URL is constructed at the moment the function is invoked.
<script setup lang="ts">
const id = ref(1)

const { data, error, refresh } = await useFetch(`/api/users/${id.value}`, {
  watch: [id]
})
</script>
如果你需要根据响应式值更改 URL,则可能需要使用 计算 URL。¥If you need to change the URL based on a reactive value, you may want to use a computed URL instead.

计算 URL

¥Computed URL有时你可能需要根据响应式值计算 URL,并在每次更改时刷新数据。你可以将每个参数作为响应式值附加,而无需费力操作。Nuxt 将自动使用响应式值,并在每次更改时重新获取。¥Sometimes you may need to compute an URL from reactive values, and refresh the data each time these change. Instead of juggling your way around, you can attach each param as a reactive value. Nuxt will automatically use the reactive value and re-fetch each time it changes.
<script setup lang="ts">
const id = ref(null)

const { data, status } = useLazyFetch('/api/user', {
  query: {
    user_id: id
  }
})
</script>
对于更复杂的 URL 构造,你可以使用回调作为返回 URL 字符串的 计算 getter。¥In the case of more complex URL construction, you may use a callback as a computed getter that returns the URL string.每次依赖发生变化时,数据都会使用新构建的 URL 进行获取。将此与 not-immediate 结合使用,你可以等到响应式元素发生变化后再进行抓取。¥Every time a dependency changes, the data will be fetched using the newly constructed URL. Combine this with not-immediate, and you can wait until the reactive element changes before fetching.
<script setup lang="ts">
const id = ref(null)

const { data, status } = useLazyFetch(() => `/api/users/${id.value}`, {
  immediate: false
})

const pending = computed(() => status.value === 'pending');
</script>

<template>
  <div>
    <!-- disable the input while fetching -->
    <input v-model="id" type="number" :disabled="pending"/>

    <div v-if="status === 'idle'">
      Type an user ID
    </div>

    <div v-else-if="pending">
      Loading ...
    </div>

    <div v-else>
      {{ data }}
    </div>
  </div>
</template>
如果你需要在其他响应式值发生变化时强制刷新,也可以使用 关注其他值。¥If you need to force a refresh when other reactive values change, you can also watch other values.

非立即生效

¥Not immediateuseFetch 可组合项将在被调用时开始获取数据。例如,你可以通过设置 immediate: false 来等待用户交互,从而避免这种情况。¥The useFetch composable will start fetching data the moment is invoked. You may prevent this by setting immediate: false, for example, to wait for user interaction.这样,你将需要 status 来处理数据获取生命周期,以及 execute 来启动数据获取。¥With that, you will need both the status to handle the fetch lifecycle, and execute to start the data fetch.
<script setup lang="ts">
const { data, error, execute, status } = await useLazyFetch('/api/comments', {
  immediate: false
})
</script>

<template>
  <div v-if="status === 'idle'">
    <button @click="execute">Get data</button>
  </div>

  <div v-else-if="status === 'pending'">
    Loading comments...
  </div>

  <div v-else>
    {{ data }}
  </div>
</template>
为了更精细地控制,status 变量可以是:¥For finer control, the status variable can be:
  • idle 表示抓取尚未开始
  • pending 表示抓取已开始但尚未完成
  • error 表示抓取失败
  • success 表示抓取成功完成
¥Passing Headers and Cookies当我们在浏览器中调用 $fetch 时,像 cookie 这样的用户标头将直接发送到 API。¥When we call $fetch in the browser, user headers like cookie will be directly sent to the API.通常,在服务器端渲染期间,出于安全考虑,$fetch 不会包含用户的浏览器 Cookie,也不会传递来自 fetch 响应的 Cookie。¥Normally, during server-side-rendering, due to security considerations, the $fetch wouldn't include the user's browser cookies, nor pass on cookies from the fetch response.然而,当使用服务器上的相对 URL 调用 useFetch 时,Nuxt 将使用 useRequestFetch 来代理 headers 和 cookies(不打算转发的 headers 除外,例如 host)。¥However, when calling useFetch with a relative URL on the server, Nuxt will use useRequestFetch to proxy headers and cookies (with the exception of headers not meant to be forwarded, like host).¥Pass Cookies From Server-side API Calls on SSR Response如果你想将 cookie 从内部请求传递/代理到客户端,则需要自行处理。¥If you want to pass on/proxy cookies in the other direction, from an internal request back to the client, you will need to handle this yourself.
composables/fetch.ts
import { appendResponseHeader } from 'h3'
import type { H3Event } from 'h3'

export const fetchWithCookie = async (event: H3Event, url: string) => {
  /* Get the response from the server endpoint */
  const res = await $fetch.raw(url)
  /* Get the cookies from the response */
  const cookies = res.headers.getSetCookie()
  /* Attach each cookie to our incoming Request */
  for (const cookie of cookies) {
    appendResponseHeader(event, 'set-cookie', cookie)
  }
  /* Return the data of the response */
  return res._data
}
<script setup lang="ts">
// This composable will automatically pass cookies to the client
const event = useRequestEvent()

const { data: result } = await useAsyncData(() => fetchWithCookie(event!, '/api/with-cookie'))

onMounted(() => console.log(document.cookie))
</script>

选项 API 支持

¥Options API supportNuxt 提供了一种在 Options API 中执行 asyncData 抓取的方法。你必须将组件定义封装在 defineNuxtComponent 中才能使其正常工作。¥Nuxt provides a way to perform asyncData fetching within the Options API. You must wrap your component definition within defineNuxtComponent for this to work.
<script>
export default defineNuxtComponent({
  /* Use the fetchKey option to provide a unique key */
  fetchKey: 'hello',
  async asyncData () {
    return {
      hello: await $fetch('/api/hello')
    }
  }
})
</script>
在 Nuxt 中,建议使用 <script setup><script setup lang="ts"> 声明 Vue 组件。
Read more in Docs > API > Utils > Define Nuxt Component.

从服务器到客户端的序列化数据

¥Serializing Data From Server to Client当使用 useAsyncDatauseLazyAsyncData 将服务器端获取的数据传输到客户端(以及任何其他使用 Nuxt 负载 的组件)时,有效负载将使用 devalue 进行序列化。这不仅使我们能够传输基本的 JSON,还能序列化和恢复/反序列化更高级的数据类型,例如正则表达式、日期、Map 和 Set、refreactiveshallowRefshallowReactiveNuxtError - 以及更多。¥When using useAsyncData and useLazyAsyncData to transfer data fetched on server to the client (as well as anything else that utilizes the Nuxt payload), the payload is serialized with devalue. This allows us to transfer not just basic JSON but also to serialize and revive/deserialize more advanced kinds of data, such as regular expressions, Dates, Map and Set, ref, reactive, shallowRef, shallowReactive and NuxtError - and more.对于 Nuxt 不支持的类型,也可以定义自己的序列化器/反序列化器。你可以在 useNuxtApp 文档中阅读更多信息。¥It is also possible to define your own serializer/deserializer for types that are not supported by Nuxt. You can read more in the useNuxtApp docs.
请注意,这不适用于使用 $fetchuseFetch 获取时从服务器路由传递的数据 - 请参阅下一节了解更多信息。

从 API 序列化数据路由

¥Serializing Data From API Routes从 server 目录获取数据时,响应会使用 JSON.stringify 进行序列化。但是,由于序列化仅限于 JavaScript 原始类型,因此 Nuxt 会尽力将 $fetchuseFetch 的返回类型转换为与实际值匹配的类型。¥When fetching data from the server directory, the response is serialized using JSON.stringify. However, since serialization is limited to only JavaScript primitive types, Nuxt does its best to convert the return type of $fetch and useFetch to match the actual value.:
Read more in https://web.nodejs.cn/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description.
了解更多关于 JSON.stringify 的限制。¥Learn more about JSON.stringify limitations.::

示例

¥Example
server/api/foo.ts
export default defineEventHandler(() => {
  return new Date()
})
app.vue
<script setup lang="ts">
// Type of `data` is inferred as string even though we returned a Date object
const { data } = await useFetch('/api/foo')
</script>

自定义序列化函数

¥Custom serializer function要自定义序列化行为,你可以在返回的对象上定义一个 toJSON 函数。如果你定义了 toJSON 方法,Nuxt 将遵循该函数的返回类型,并且不会尝试转换类型。¥To customize the serialization behavior, you can define a toJSON function on your returned object. If you define a toJSON method, Nuxt will respect the return type of the function and will not try to convert the types.
server/api/bar.ts
export default defineEventHandler(() => {
  const data = {
    createdAt: new Date(),

    toJSON() {
      return {
        createdAt: {
          year: this.createdAt.getFullYear(),
          month: this.createdAt.getMonth(),
          day: this.createdAt.getDate(),
        },
      }
    },
  }
  return data
})
app.vue
<script setup lang="ts">
// Type of `data` is inferred as
// {
//   createdAt: {
//     year: number
//     month: number
//     day: number
//   }
// }
const { data } = await useFetch('/api/bar')
</script>

使用替代序列化器

¥Using an alternative serializerNuxt 目前不支持 JSON.stringify 的替代序列化器。但是,你可以将有效负载返回为普通字符串,并使用 toJSON 方法来维护类型安全。¥Nuxt does not currently support an alternative serializer to JSON.stringify. However, you can return your payload as a normal string and utilize the toJSON method to maintain type safety.在下面的示例中,我们使用 superjson 作为序列化器。¥In the example below, we use superjson as our serializer.
server/api/superjson.ts
import superjson from 'superjson'

export default defineEventHandler(() => {
  const data = {
    createdAt: new Date(),

    // Workaround the type conversion
    toJSON() {
      return this
    }
  }

  // Serialize the output to string, using superjson
  return superjson.stringify(data) as unknown as typeof data
})
app.vue
<script setup lang="ts">
import superjson from 'superjson'

// `date` is inferred as { createdAt: Date } and you can safely use the Date object methods
const { data } = await useFetch('/api/superjson', {
  transform: (value) => {
    return superjson.parse(value as unknown as string)
  },
})
</script>

Recipes

通过 POST 请求使用 SSE(服务器发送事件)

¥Consuming SSE (Server Sent Events) via POST request
如果你通过 GET 请求使用 SSE,则可以使用 EventSource 或 VueUse 可组合 useEventSource
通过 POST 请求使用 SSE 时,你需要手动处理连接。以下是操作方法:¥When consuming SSE via POST request, you need to handle the connection manually. Here's how you can do it:
// Make a POST request to the SSE endpoint
const response = await $fetch<ReadableStream>('/chats/ask-ai', {
  method: 'POST',
  body: {
    query: "Hello AI, how are you?",
  },
  responseType: 'stream',
})

// Create a new ReadableStream from the response with TextDecoderStream to get the data as text
const reader = response.pipeThrough(new TextDecoderStream()).getReader()

// Read the chunk of data as we get it
while (true) {
  const { value, done } = await reader.read()

  if (done)
    break

  console.log('Received:', value)
}

并行请求

¥Making parallel requests当请求彼此不依赖时,你可以将它们与 Promise.all() 并行运行以提高性能。¥When requests don't rely on each other, you can make them in parallel with Promise.all() to boost performance.
const { data } = await useAsyncData(() => {
  return Promise.all([
    $fetch("/api/comments/"), 
    $fetch("/api/author/12")
  ]);
});

const comments = computed(() => data.value?.[0]);
const author = computed(() => data.value?.[1]);