简言
记录下如何使用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元数据,我的解决方法是:
- url的useFetch的url使用完整的http/https请求(http://.xxxx/xxx)这种,不完整的你查看页面源代码会发现区别。
- 然后key属性使用uuid或时间戳(解决f5刷新不会调用数据,不要 使用 await nextTick();)
- 正常使用即可。
这是我现在知道的唯一暂行方法,不知道原因,有知道其他方法的可以评论告诉我,谢谢。
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等第三方请求库。
标签:内置,请求,Nuxt3,useFetch,使用,useAsyncData,fetch,pending,客户端 From: https://blog.csdn.net/qq_43231248/article/details/137146419