使用 Vue & UI Pro 的仪表板模板

数据获取

Nuxt 提供可组合函数来处理应用程序中的数据获取。

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

简而言之

  • useFetch 是在组件设置函数中处理数据获取的最直接方法。
  • $fetch 非常适合根据用户交互进行网络请求。
  • useAsyncData$fetch 结合使用,可以提供更细粒度的控制。

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

在此之前,必须了解这些可组合函数存在的根本原因。

为什么要使用特定的可组合函数?

当使用像 Nuxt 这样的框架时,它可以在客户端和服务器环境中执行调用并渲染页面,因此必须解决一些挑战。这就是为什么 Nuxt 提供可组合函数来包装您的查询,而不是让开发人员仅仅依赖于 $fetch 调用。

网络调用重复

useFetchuseAsyncData 可组合函数确保在服务器上进行 API 调用后,数据会正确地转发到有效负载中的客户端。

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

使用 Nuxt DevTools有效负载选项卡 中检查此数据。

悬念

Nuxt 在幕后使用 Vue 的 <Suspense> 组件,以防止在所有异步数据都可用于视图之前进行导航。数据获取可组合函数可以帮助您利用此功能,并在每个调用基础上使用最适合的功能。

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

useFetch

useFetch 可组合函数是执行数据获取的最直接方法。

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

此组合函数是对 useAsyncData 组合函数和 $fetch 工具的包装。

文档 > API > 组合函数 > 使用 Fetch 中了解更多信息。
文档 > 示例 > 功能 > 数据获取 中阅读和编辑实时示例。

$fetch

Nuxt 包含 ofetch 库,并在整个应用程序中作为 $fetch 别名自动导入。它是 useFetch 在幕后使用的工具。

pages/todos.vue
<script setup lang="ts">
async function 
addTodo
() {
const
todo
= await
$fetch
('/api/todos', {
method
: 'POST',
body
: {
// My todo data } }) } </script>
请注意,仅使用 $fetch 不会提供 网络调用去重和导航阻止
建议在客户端交互(基于事件)中使用 $fetch,或者与 useAsyncData 结合使用,以获取初始组件数据。
了解更多关于 $fetch 的信息。

useAsyncData

组合函数 useAsyncData 负责包装异步逻辑,并在解析后返回结果。

useFetch(url) 几乎等同于 useAsyncData(url, () => $fetch(url))
这是针对最常见用例的开发者体验糖衣。

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

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 时。

设置键对于使用 useNuxtData 在组件之间共享相同数据或 刷新特定数据 很有用。
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 请求完成,然后处理结果的绝佳方式。

<script setup lang="ts">
const { data: discounts, pending } = 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 的内容。

返回值

useFetchuseAsyncData 具有相同的返回值,如下所示。

  • data:传递进来的异步函数的结果。
  • pending:一个布尔值,指示数据是否仍在获取中。
  • refresh/execute:一个函数,可用于刷新 handler 函数返回的数据。
  • clear:一个函数,可用于将 data 设置为 undefined,将 error 设置为 null,将 pending 设置为 false,将 status 设置为 idle,并将任何当前正在进行的请求标记为已取消。
  • error:如果数据获取失败,则为错误对象。
  • status: 表示数据请求状态的字符串 ("idle", "pending", "success", "error")。
data, pending, errorstatus 是 Vue refs,可以在 <script setup> 中使用 .value 访问。

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

如果你没有在服务器端获取数据(例如,使用 server: false),那么数据将不会在水合完成之前获取。这意味着即使你在客户端等待 useFetchdata<script setup> 中仍然会保持为 null。

选项

useAsyncDatauseFetch 返回相同的对象类型,并接受一组通用的选项作为最后一个参数。它们可以帮助你控制可组合项的行为,例如导航阻止、缓存或执行。

延迟

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

app.vue
<script setup lang="ts">
const { 
pending
,
data
:
posts
} =
useFetch
('/api/posts', {
lazy
: true
}) </script> <template> <!-- you will need to handle a loading state --> <
div
v-if="
pending
">
Loading ... </
div
>
<
div
v-else>
<
div
v-for="
post
in
posts
">
<!-- do something --> </
div
>
</
div
>
</template>

或者,你可以使用 useLazyFetchuseLazyAsyncData 作为方便的方法来执行相同的操作。

<script setup lang="ts">
const { 
pending
,
data
:
posts
} =
useLazyFetch
('/api/posts')
</script>
阅读更多关于 useLazyFetch 的内容。
阅读更多关于 useLazyAsyncData 的内容。

仅客户端获取

默认情况下,数据获取可组合项将在客户端和服务器环境中执行其异步函数。将 server 选项设置为 false 以仅在客户端执行调用。在初始加载时,数据在水合完成之前不会获取,因此你必须处理一个待处理状态,尽管在后续的客户端导航中,数据将在加载页面之前等待。

结合 lazy 选项,这对于在第一次渲染时不需要的数据(例如,非 SEO 敏感数据)很有用。

/* This call is performed before hydration */
const 
articles
= await
useFetch
('/api/article')
/* This call will only be performed on the client */ const {
pending
,
data
:
posts
} =
useFetch
('/api/comments', {
lazy
: true,
server
: false
})

可组合函数 useFetch 应该在 setup 方法中调用,或直接在生命周期钩子函数的顶层调用,否则应该使用 $fetch 方法

最小化有效负载大小

选项 pick 可帮助您通过仅选择要从可组合函数返回的字段来最小化存储在 HTML 文档中的有效负载大小。

<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 函数来更改查询结果。

const { data: mountains } = await useFetch('/api/mountains', {
  transform: (mountains) => {
    return mountains.map(mountain => ({ title: mountain.title, description: mountain.description }))
  }
})
picktransform 不会阻止最初获取不需要的数据。但它们会阻止将不需要的数据添加到从服务器传输到客户端的有效负载中。

缓存和重新获取

useFetchuseAsyncData 使用键来防止重新获取相同的数据。

  • useFetch 使用提供的 URL 作为键。或者,可以在作为最后一个参数传递的 options 对象中提供 key 值。
  • useAsyncData 如果第一个参数是字符串,则使用第一个参数作为键。如果第一个参数是执行查询的处理程序函数,则会为您生成一个对 useAsyncData 实例的文件名和行号唯一的键。
要通过键获取缓存数据,可以使用 useNuxtData

刷新和执行

如果您想手动获取或刷新数据,请使用可组合函数提供的 executerefresh 函数。

<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>

函数 executerefresh 的别名,它们的工作方式完全相同,但在获取 不是立即 的情况下,它更具语义。

要全局重新获取或使缓存数据失效,请参见 clearNuxtDatarefreshNuxtData

清除

如果您想出于任何原因清除提供的数据,而无需知道要传递给 clearNuxtData 的特定键,可以使用 composables 提供的 clear 函数。

<script setup lang="ts">
const { 
data
,
clear
} = await
useFetch
('/api/users')
const
route
=
useRoute
()
watch
(() =>
route
.
path
, (
path
) => {
if (
path
=== '/')
clear
()
}) </script>

观察

要使您的获取函数在应用程序中的其他响应式值发生变化时每次都重新运行,请使用 watch 选项。您可以将其用于一个或多个watchable 元素。

<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。例如,这将继续获取用户的相同初始 ID,因为 URL 是在函数被调用时构建的。

<script setup lang="ts">
const id = ref(1)

const { data, error, refresh } = await useFetch(`/api/users/${id.value}`, {
  watch: [id]
})
</script>

如果您需要根据响应式值更改 URL,您可能需要使用 计算的 URL

计算的 URL

有时您可能需要从响应式值计算 URL,并在这些值每次发生变化时刷新数据。与其费力地处理,您可以将每个参数附加为响应式值。Nuxt 将自动使用响应式值并在每次更改时重新获取。

<script setup lang="ts">
const id = ref(null)

const { data, pending } = useLazyFetch('/api/user', {
  query: {
    user_id: id
  }
})
</script>

在更复杂的 URL 构建的情况下,您可以使用回调作为 计算的 getter,它返回 URL 字符串。

每次依赖项发生变化时,都会使用新构建的 URL 获取数据。将此与 非立即 结合使用,您可以在响应式元素更改之前等待获取。

<script setup lang="ts">
const id = ref(null)

const { data, pending, status } = useLazyFetch(() => `/api/users/${id.value}`, {
  immediate: false
})
</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>

如果您需要在其他响应式值发生变化时强制刷新,您也可以 观察其他值

非立即

useFetch composable 将在调用时立即开始获取数据。您可以通过设置 immediate: false 来阻止这种情况,例如,等待用户交互。

这样,您将需要 status 来处理获取生命周期,以及 execute 来启动数据获取。

<script setup lang="ts">
const { data, error, execute, pending, 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="pending">
    Loading comments...
  </div>

  <div v-else>
    {{ data }}
  </div>
</template>

为了更精细的控制,status 变量可以

  • idle 当获取操作尚未开始时
  • pending 当获取操作已开始但尚未完成时
  • error 当获取操作失败时
  • success 当获取操作成功完成时

传递 Headers 和 Cookies

当我们在浏览器中调用 $fetch 时,像 cookie 这样的用户 Headers 会直接发送到 API。但在服务器端渲染期间,由于 $fetch 请求是在服务器内部进行的,因此它不包含用户的浏览器 Cookies,也不传递来自获取响应的 Cookies。

将客户端 Headers 传递到 API

我们可以使用 useRequestHeaders 从服务器端访问和代理 Cookies 到 API。

下面的示例将请求 Headers 添加到一个同构的 $fetch 调用中,以确保 API 端点可以访问用户最初发送的相同的 cookie Header。

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

const { data } = await useFetch('/api/me', { headers })
</script>
在将 Headers 代理到外部 API 之前,请务必谨慎,只包含您需要的 Headers。并非所有 Headers 都可以安全地绕过,可能会导致意外行为。以下是一些不应代理的常见 Headers 列表:
  • host, accept
  • content-length, content-md5, content-type
  • x-forwarded-host, x-forwarded-port, x-forwarded-proto
  • cf-connecting-ip, cf-ray

在 SSR 响应中传递来自服务器端 API 调用的 Cookies

如果您想将 Cookies 传递到另一个方向,即从内部请求传递回客户端,则需要自己处理。

composables/fetch.ts
import { appendResponseHeader, 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.get('set-cookie') || '').split(',')
  /* 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>

Options API 支持

Nuxt 3 提供了一种在 Options API 中执行 asyncData 获取操作的方法。您必须将组件定义包装在 defineNuxtComponent 中才能使此方法生效。

<script>
export default defineNuxtComponent({
  /* Use the fetchKey option to provide a unique key */
  fetchKey: 'hello',
  async asyncData () {
    return {
      hello: await $fetch('/api/hello')
    }
  }
})
</script>
使用 <script setup><script setup lang="ts"> 是在 Nuxt 3 中声明 Vue 组件的推荐方法。
文档 > API > 工具 > 定义 Nuxt 组件 中了解更多信息。

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

当使用 useAsyncDatauseLazyAsyncData 将服务器端获取的数据传输到客户端(以及任何其他使用 Nuxt 负载 的内容)时,负载将使用 devalue 进行序列化。这使我们能够传输不仅仅是基本的 JSON,还可以序列化和恢复/反序列化更高级类型的数据,例如正则表达式、日期、Map 和 Set、refreactiveshallowRefshallowReactiveNuxtError - 等等。

也可以为 Nuxt 不支持的类型定义自己的序列化器/反序列化器。您可以在 useNuxtApp 文档中了解更多信息。

请注意,这不适用于 使用 $fetchuseFetch 从服务器路由传递的数据 - 请参阅下一节以获取更多信息。

从 API 路由序列化数据

server 目录获取数据时,响应将使用 JSON.stringify 进行序列化。但是,由于序列化仅限于 JavaScript 原语类型,因此 Nuxt 会尽力将 $fetchuseFetch 的返回类型转换为与实际值匹配的类型。

了解有关 JSON.stringify 限制的更多信息。

示例

server/api/foo.ts
export default defineEventHandler(() => {
  return new Date()
})
app.vue
<script setup