【Nuxt3】使用内置方法获取网络数据和使用场景

简言

记录下如何使用useFetch和$fetch的使用方法和它们的使用场景。

获取数据

nuxt3内置了很多方法来获取网络数据。
这些方法有:

  • useAsyncData — useAsyncData 可以访问以 SSR 友好的可组合方式异步解析的数据。
  • useFetch — 使用 SSR 友好型可组合程序从 API 端点获取数据。
  • $fetch — Nuxt 使用 ofetch 在全局范围内公开 $fetch 辅助程序,用于执行 HTTP 请求。
  • useLazyAsyncData — 这个围绕 useAsyncData 的包装器会立即触发导航,useAsyncData的lazy为true版本。
  • useLazyFetch — 这个围绕 useFetch 的包装器会立即触发导航,useFetch的lazy为true版本。

主要是前三个常用,useAsyncData和$fetch结合使用可以在服务器端数据数据和水合,useFecth则是两者的语法糖,$fetch是客户端的http请求,获取数据后在客户端进行水合,后面两个看场景,需要立即导航时可以使用,

useAsyncData

在页面、组件和插件中,您可以使用 useAsyncData 来访问异步解析的数据。

useAsyncData 是一种可组合程序,可在 Nuxt 上下文中直接调用。它能返回反应式可合成数据,并处理将响应添加到 Nuxt 有效载荷的过程,这样就能将响应从服务器传递到客户端,而无需在页面水合时在客户端重新获取数据。

示例:

<script setup lang="ts">
const { data, pending, error, refresh } = await useAsyncData(
  'mountains',
  () => $fetch('https://api.nuxtjs.dev/mountains')
)
</script>

data、pending、status 和 error 是 Vue 的引用,在<script setup> 中使用时应使用 .value 进行访问,而 refresh/execute 是用于重新获取数据的普通函数。

参数

useAsyncData()可以接收三个参数:

  • key — 唯一的密钥,以确保在不同请求中正确地重复获取数据。如果不提供密钥,系统将为您生成与 useAsyncData 实例的文件名和行号唯一的密钥,建议添加
  • handler — 是一个异步函数,必须返回一个真实值(例如,不能是未定义的或空值),否则请求可能会在客户端被重复执行。
  • options — 配置选项对象,有以下属性:
    • server — 是否在服务器上获取数据(默认为 true)。
    • lazy — 是否在加载路由后解析异步函数,而不是阻止客户端导航(默认为 false)。
    • immediate — 设置为 false 时,将阻止请求立即触发。(默认为 true)。
    • default工厂函数,用于在异步函数解析前设置数据的默认值–在使用 lazy: true 或 immediate: false 选项时非常有用
    • transform — 函数,用于在解析后更改处理程序函数结果。
    • getCachedData — 提供一个返回缓存数据的函数。如果返回值为空或未定义,则会触发一次获取。默认情况下,这是:key => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key],只有在启用 payloadExtraction 时才会缓存数据。
    • pick — 只从处理函数结果中选取数组中的指定键。
    • watch — 监视被动源自动刷新。
    • deep返回深度 ref 对象中的数据(默认为 true)。如果数据不需要深度反应,可以将其设置为 false,以浅层 ref 对象返回数据,这样可以提高性能
    • dedupe — 避免一次多次获取同一按键(默认为取消)。可选值有:
      • cancel — 在有新申请时取消现有申请。
      • defer —如果有悬而未决的请求,则根本不会提出新请求。

第三个参数,watch、default是常用的。

返回值

返回值是一个异步函数对象(Promise函数对象)。

  • data — 传入的异步函数的结果。

  • pending ---- 布尔值,表示是否仍在获取数据。

  • refresh/execute — 函数,用于刷新处理程序函数返回的数据。

  • error — 如果数据获取失败,则返回错误对象。

  • status ---- 表示数据请求状态的字符串(“idle”, “pending”, “success”, “error”)。

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

示例

<script setup lang="ts">
/* Navigation will occur before fetching is complete.
  Handle pending and error states directly within your component's template
*/
const { pending, data: count } = await useAsyncData(
  "count",
  () => new Promise((resolve) => setTimeout(() => resolve(100), 1000)),
  {
    default: () => 0,
  }
);
console.log(pending.value);

watch(count, (newCount) => {
  // Because count might start out null, you won't have access
  // to its contents immediately, but you can watch it.
  console.log(newCount);
});
</script>

<template>
  <div>
    {{ pending ? "Loading" : count }}
  </div>
</template>

执行多个请求:

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

$fetch

Nuxt 包含 ofetch 库,并作为 $fetch 别名在应用程序中全局自动导入。这也是 useFetch 在幕后使用的。

请注意,仅使用 $fetch 无法实现网络调用的重复删除和导航预防。
建议在客户端交互(基于事件)中使用 $fetch,或在获取初始组件数据时结合使用 useAsyncData。

在组件中使用 $fetch 而不使用 useAsyncData 对其进行包装会导致获取数据两次:最初在服务器上,然后在水合过程中再次在客户端,因为 $fetch 不会将状态从服务器传输到客户端。因此,由于客户端必须再次获取数据,因此获取将在两侧执行。

我们建议在获取组件数据时使用 useFetch 或 useAsyncData + $fetch 以防止重复获取数据。

== 上面说的组件其实是页面,也就是说如果是一个页面导航,建议使用useFetch,如果你的数据不触发导航调用,可以在事件中使用$fetch,因为那时候是客户端调用,不会触发服务端渲染 ==

您可以在任何仅在客户端执行的方法中使用 $fetch。

<script setup lang="ts">
function contactForm() {
  $fetch('/api/contact', {
    method: 'POST',
    body: { hello: 'world '}
  })
}
</script>

<template>
  <button @click="contactForm">Contact</button>
</template>

useFetch

该可组合程序为 useAsyncData 和 $fetch 提供了方便的封装。它根据 URL 和获取选项自动生成密钥,根据服务器路由为请求 url 提供类型提示,并推断 API 响应类型。

useFetch 是一种可组合程序,可在设置函数、插件或路由中间件中直接调用。它能返回反应式可组合程序,并将响应添加到 Nuxt 有效载荷中,以便从服务器传递到客户端,而无需在页面水合时在客户端重新抓取数据。

您还可以使用拦截器:

const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
  onRequest({ request, options }) {
    // Set the request headers
    options.headers = options.headers || {}
    options.headers.authorization = '...'
  },
  onRequestError({ request, options, error }) {
    // Handle the request errors
  },
  onResponse({ request, response, options }) {
    // Process the response data
    localStorage.setItem('token', response._data.token)
  },
  onResponseError({ request, response, options }) {
    // Handle the response errors
  }
})

可以封装一个自定义请求方法,注意不要同名

参数

useFetch()接收三个参数:

  • url — 要获取的 URL。
  • Options1(扩展 unjs/ofetch 选项和 AsyncDataOptions)
    • method — 请求方法。
    • query — 使用ufo 在 URL 中添加查询搜索参数。
    • params — query的别名。
    • body — 请求正文(请求体),会自动字符串化(如果传递的是对象)。
    • headers — 请求标头。
    • baseURL — 请求的基本 URL。
    • timeout — 自动放弃请求的毫秒数。

所有获取选项都可以给出一个计算值或 ref 值。这些值一旦更新,就会受到监视,并自动使用任何新值发出新请求。

  • Options2 (来自 useAsyncData) — 这个选项就是useAsyncData的第一个参数 + 第三个参数 。

返回值

返回值和useAsyncData返回值类型相同。

function useFetch<DataT, ErrorT>(
  url: string | Request | Ref<string | Request> | () => string | Request,
  options?: UseFetchOptions<DataT>
): Promise<AsyncData<DataT, ErrorT>>

type UseFetchOptions<DataT> = {
  key?: string
  method?: string
  query?: SearchParams
  params?: SearchParams
  body?: RequestInit['body'] | Record<string, any>
  headers?: Record<string, string> | [key: string, value: string][] | Headers
  baseURL?: string
  server?: boolean
  lazy?: boolean
  immediate?: boolean
  getCachedData?: (key: string, nuxtApp: NuxtApp) => DataT
  deep?: boolean
  dedupe?: 'cancel' | 'defer'
  default?: () => DataT
  transform?: (input: DataT) => DataT | Promise<DataT>
  pick?: string[]
  watch?: WatchSource[] | false
}

type AsyncData<DataT, ErrorT> = {
  data: Ref<DataT | null>
  pending: Ref<boolean>
  refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
  execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
  clear: () => void
  error: Ref<ErrorT | null>
  status: Ref<AsyncDataRequestStatus>
}

interface AsyncDataExecuteOptions {
  dedupe?: 'cancel' | 'defer'
}

type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'

示例

import type { UseFetchOptions, } from "#app"
import { defu } from 'defu'
import { MyHttpResponse } from '~/types/common'
import type { KeysOf, PickFrom, _AsyncData } from "nuxt/dist/app/composables/asyncData";
import type { FetchError, } from 'ofetch';
export async function useCustomFetch<T extends MyHttpResponse>(url: string | (() => string), options: UseFetchOptions<T> = {}): Promise<_AsyncData<PickFrom<T, KeysOf<T>> | null, FetchError<T> | null>> {
  const userAuth = useCookie('token').value
  const config = useRuntimeConfig()
  url = (config.public.apiBase || '/api') + url

  const defaults: UseFetchOptions<T> = {
    baseURL: config.public.apiBase as string ?? 'http:192.168.9.70:3000/api/', //  公共路径,
    key: url + Date.now(),
    headers: userAuth ?
      { Authorization: userAuth }
      : {},
    onResponse(_ctx) {

    },
    onResponseError(_ctx) {

    }
  }

  const params = defu(options, defaults)

  const res = await useFetch(url, params)

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

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

注意!!!

useFetch()会在服务端请求数据然后水合,若请求失败会在客户端进行再次请求。
key值不同也会请求两次(服务端一次,客户端一次)。

如果是刷新(F5)则会触发服务端渲染并进行上述情况,不刷新页面触发vue-router导航,则只会进行客户端触发请求

另外,如果你想使用useFetch()获取请求后更新页面头部meta元数据,我的解决方法是:

  1. url的useFetch的url使用完整的http/https请求(http://.xxxx/xxx)这种,不完整的你查看页面源代码会发现区别。
  2. 然后key属性使用uuid或时间戳(解决f5刷新不会调用数据,不要 使用 await nextTick();)
  3. 正常使用即可。

这是我现在知道的唯一暂行方法,不知道原因,有知道其他方法的可以评论告诉我,谢谢。

在这里插入图片描述

useLazyAsyncData

默认情况下,useAsyncData 会阻止导航,直到其异步处理程序被解析。useLazyAsyncData 提供了一个围绕 useAsyncData 的包装器,通过将 lazy 选项设置为 true,可以在处理程序解析之前触发导航。

<script setup lang="ts">
/* Navigation will occur before fetching is complete.
  Handle pending and error states directly within your component's template
*/
const { pending, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))

watch(count, (newCount) => {
  // Because count might start out null, you won't have access
  // to its contents immediately, but you can watch it.
})
</script>

<template>
  <div>
    {{ pending ? 'Loading' : count }}
  </div>
</template>

useLazyFetch

默认情况下,useFetch 会阻止导航,直到其异步处理程序解析完毕。useLazyFetch 提供了一个围绕 useFetch 的封装,通过将 lazy 选项设置为 true,可以在处理程序解析完毕之前触发导航。

<script setup lang="ts">
/* Navigation will occur before fetching is complete.
  Handle pending and error states directly within your component's template
*/
const { pending, data: posts } = await useLazyFetch('/api/posts')
watch(posts, (newPosts) => {
  // Because posts might start out null, you won't have access
  // to its contents immediately, but you can watch it.
})
</script>

<template>
  <div v-if="pending">
    Loading ...
  </div>
  <div v-else>
    <div v-for="post in posts">
      <!-- do something -->
    </div>
  </div>
</template>

结语

这些方法获取数据够用了,不建议额外使用axios或request等第三方请求库。

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZSK6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值