Skip to content

数据获取

Nuxt 提供了组合式函数来处理应用中的数据获取。

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

简而言之:

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

useFetchuseAsyncData 共享一套通用的选项和模式

HTML水合

HTML 水合 是指一个过程:在客户端(浏览器)将 JavaScript 逻辑(如交互事件处理程序、状态、生命周期等)“附加”或“注入”到由服务端发送来的静态 HTML 页面中的过程。

你可以把它想象成一个“复活”或“赋予生命”的过程:

  • 服务端 生成了一个完整的、但却是“静态”的 HTML 结构。它就像一具没有灵魂的躯体。
  • 客户端 接收到这个 HTML 并立即展示给用户(这带来了快速的初始加载体验)。
  • 随后,客户端下载的 JavaScript 包开始执行。这个 JavaScript 包包含了创建交互式应用程序所需的所有逻辑。
  • 这个 JavaScript 会“接管”现有的静态 HTML,将其与背后的虚拟 DOM、组件树、状态管理等进行关联,并为其绑定事件监听器(如 onClick, onChange)。
  • 至此,原本静态的页面就被“水合”了——它变得完全可交互,就像一个正常的单页面应用(SPA)一样。

为什么需要useFetch和useAsyncData

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

useFetchuseAsyncData 通过确保如果在服务器上调用了 API,则数据会通过负载转发给客户端,从而解决了这个问题。

负载是一个 JavaScript 对象,通过 useNuxtApp().payload 访问。它在客户端用于避免在浏览器执行代码水合期间重复获取相同数据。

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

async function handleFormSubmit() {
  const res = await $fetch('/api/submit', {
    method: 'POST',
    body: {
      // 我的表单数据
    }
  })
}
</script>

<template>
  <div v-if="data == null">
    无数据
  </div>
  <div v-else>
    <form @submit="handleFormSubmit">
      <!-- 表单输入标签 -->
    </form>
  </div>
</template>

在上面的示例中,useFetch 会确保请求发生在服务器端,并正确转发到浏览器。$fetch 没有此机制,更适合仅在浏览器端发起请求的场景。

$fetch

Nuxt已内置了ofetch库,并全局导入成$fetch

vue
<script setup lang="ts">
async function addTodo() {
  const todo = await $fetch('/api/todos', {
    method: 'POST',
    body: {
      // 我的待办数据
    }
  })
}
</script>

向API传递客户端请求头

看不懂啊

useFetch

useFetch 组合函数基于 $fetch

useFetch 会在 服务端渲染 (SSR) 时自动调用,返回的数据会直接注入到 HTML 中。

如果请求的 URL 是相对路径 /api/hello,Nuxt 会自动代理到服务端 API。

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

<template>
  <p>页面访问次数:{{ count }}</p>
</template>

useAsyncData

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

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

返回值

useFetchuseAsyncData 返回相同的对象,包含如下内容:

  • data: 传入的异步函数结果。
  • refresh/execute: 用于刷新由 handler 函数返回的数据。
  • clear: 用于将 data 设为 undefined(或提供的 options.default() 的值),error 设为 nullstatus 设为 idle,并将当前所有待定请求标记为取消。
  • error: 数据获取失败时的错误对象。
  • status: 表示数据请求状态的字符串("idle""pending""success""error")。

警告

dataerrorstatus 是 Vue 的 ref,在 <script setup> 中用 .value 访问。