useFetch
响应式 Fetch API 提供了中止请求、在请求发送前拦截请求、当 URL 变化时自动重新请求、以及创建带有预定义选项的 useFetch
功能的能力。
TIP
在使用 Nuxt 3 时,此功能将 不会 自动导入,而是使用 Nuxt 内置的 useFetch()
。如果您想使用 VueUse 中的函数,请使用显式导入。
示例
https://httpbin.org/get
https://httpbin.org/delay/10
http://httpbin.org/status/500
isFinished: false isFetching: false canAbort: false statusCode: null error: null data: null
用法
基本用法
useFetch
函数可以通过简单提供一个 URL 来使用。URL 可以是一个字符串或一个 ref
。data
对象将包含请求的结果,error
对象将包含任何错误,而 isFetching
对象将指示请求是否正在加载。
import { useFetch } from '@vueuse/core'
const { isFetching, error, data } = useFetch(url)
异步用法
useFetch
也可以像普通的 fetch 一样被 await。请注意,任何异步组件都必须将使用它的组件包裹在 <Suspense>
标签中。您可以在 官方 Vue 3 文档 中了解更多关于 suspense API 的信息。
import { useFetch } from '@vueuse/core'
const { isFetching, error, data } = await useFetch(url)
URL 变化时重新请求
使用 ref
作为 URL 参数将允许 useFetch
函数在 URL 更改时自动触发另一个请求。
const url = ref('https://my-api.com/user/1')
const { data } = useFetch(url, { refetch: true })
url.value = 'https://my-api.com/user/2' // 将触发另一个请求
防止请求立即发送
将 immediate
选项设置为 false 将防止请求在调用 execute
函数之前发送。
const { execute } = useFetch(url, { immediate: false })
execute()
中止请求
可以使用 useFetch
函数中的 abort
函数中止请求。canAbort
属性指示请求是否可以中止。
const { abort, canAbort } = useFetch(url)
setTimeout(() => {
if (canAbort.value)
abort()
}, 100)
也可以通过使用 timeout
属性自动中止请求。当达到给定的超时时间时,它将调用 abort
函数。
const { data } = useFetch(url, { timeout: 100 })
拦截请求
beforeFetch
选项可以在请求发送之前拦截请求并修改请求选项和 URL。
const { data } = useFetch(url, {
async beforeFetch({ url, options, cancel }) {
const myToken = await getMyToken()
if (!myToken)
cancel()
options.headers = {
...options.headers,
Authorization: `Bearer ${myToken}`,
}
return {
options,
}
},
})
afterFetch
选项可以在响应数据更新之前拦截响应数据。
const { data } = useFetch(url, {
afterFetch(ctx) {
if (ctx.data.title === 'HxH')
ctx.data.title = 'Hunter x Hunter' // 修改响应数据
return ctx
},
})
onFetchError
选项可以在将响应数据和错误更新之前拦截它们,前提是 updateDataOnError
设置为 true
。
const { data } = useFetch(url, {
updateDataOnError: true,
onFetchError(ctx) {
// ctx.data 在 5xx 响应时可能为 null
if (ctx.data === null)
ctx.data = { title: 'Hunter x Hunter' } // 修改响应数据
ctx.error = new Error('自定义错误') // 修改错误
return ctx
},
})
console.log(data.value) // { title: 'Hunter x Hunter' }
设置请求方法和返回类型
通过在 useFetch
的末尾添加适当的方法可以设置请求方法和返回类型。
// 请求将使用 GET 方法发送,并且数据将解析为 JSON
const { data } = useFetch(url).get().json()
// 请求将使用 POST 方法发送,并且数据将解析为文本
const { data } = useFetch(url).post().text()
// 或使用选项设置方法
// 请求将使用 GET 方法发送,并且数据将解析为 blob
const { data } = useFetch(url, { method: 'GET' }, { refetch: true }).blob()
创建自定义实例
createFetch
函数将返回一个带有任何预配置选项的 useFetch 函数。这对于在使用相同基础 URL 或需要授权头的应用程序中与 API 进行交互非常有用。
const useMyFetch = createFetch({
baseUrl: 'https://my-api.com',
options: {
async beforeFetch({ options }) {
const myToken = await getMyToken()
options.headers.Authorization = `Bearer ${myToken}`
return { options }
},
},
fetchOptions: {
mode: 'cors',
},
})
const { isFetching, error, data } = useMyFetch('users')
如果您想在预配置实例和新生成实例之间控制 beforeFetch
、afterFetch
和 onFetchError
的行为,您可以提供一个 combination
选项以切换 overwrite
或 chaining
。
const useMyFetch = createFetch({
baseUrl: 'https://my-api.com',
combination: 'overwrite',
options: {
// 仅当新生成实例未传递 beforeFetch 时,预配置实例中的 beforeFetch 将运行
async beforeFetch({ options }) {
const myToken = await getMyToken()
options.headers.Authorization = `Bearer ${myToken}`
return { options }
},
},
})
// 使用 useMyFetch 的 beforeFetch
const { isFetching, error, data } = useMyFetch('users')
// 使用自定义 beforeFetch
const { isFetching, error, data } = useMyFetch('users', {
async beforeFetch({ url, options, cancel }) {
const myToken = await getMyToken()
if (!myToken)
cancel()
options.headers = {
...options.headers,
Authorization: `Bearer ${myToken}`,
}
return {
options,
}
},
})
您可以通过在 afterFetch
或 onFetchError
中调用 execute
方法来重新执行请求。以下是刷新令牌的简单示例:
let isRefreshing = false
const refreshSubscribers: Array<() => void> = []
const useMyFetch = createFetch({
baseUrl: 'https://my-api.com',
options: {
async beforeFetch({ options }) {
const myToken = await getMyToken()
options.headers.Authorization = `Bearer ${myToken}`
return { options }
},
afterFetch({ data, response, context, execute }) {
if (needRefreshToken) {
if (!isRefreshing) {
isRefreshing = true
refreshToken().then((newToken) => {
if (newToken.value) {
isRefreshing = false
setMyToken(newToken.value)
onRrefreshed()
}
else {
refreshSubscribers.length = 0
// 处理刷新令牌错误
}
})
}
return new Promise((resolve) => {
addRefreshSubscriber(() => {
execute().then((response) => {
resolve({ data, response })
})
})
})
}
return { data, response }
},
// 或使用 onFetchError 和 updateDataOnError
updateDataOnError: true,
onFetchError({ error, data, response, context, execute }) {
// 同样与 afterFetch
return { error, data }
},
},
fetchOptions: {
mode: 'cors',
},
})
async function refreshToken() {
const { data, execute } = useFetch<string>('refresh-token', {
immediate: false,
})
await execute()
return data
}
function onRrefreshed() {
refreshSubscribers.forEach(callback => callback())
refreshSubscribers.length = 0
}
function addRefreshSubscriber(callback: () => void) {
refreshSubscribers.push(callback)
}
const { isFetching, error, data } = useMyFetch('users')
let isRefreshing = false
const refreshSubscribers = []
const useMyFetch = createFetch({
baseUrl: 'https://my-api.com',
options: {
async beforeFetch({ options }) {
const myToken = await getMyToken()
options.headers.Authorization = `Bearer ${myToken}`
return { options }
},
afterFetch({ data, response, context, execute }) {
if (needRefreshToken) {
if (!isRefreshing) {
isRefreshing = true
refreshToken().then((newToken) => {
if (newToken.value) {
isRefreshing = false
setMyToken(newToken.value)
onRrefreshed()
} else {
refreshSubscribers.length = 0
// 处理刷新令牌错误
}
})
}
return new Promise((resolve) => {
addRefreshSubscriber(() => {
execute().then((response) => {
resolve({ data, response })
})
})
})
}
return { data, response }
},
// 或使用 onFetchError 和 updateDataOnError
updateDataOnError: true,
onFetchError({ error, data, response, context, execute }) {
// 同样与 afterFetch
return { error, data }
},
},
fetchOptions: {
mode: 'cors',
},
})
async function refreshToken() {
const { data, execute } = useFetch('refresh-token', {
immediate: false,
})
await execute()
return data
}
function onRrefreshed() {
refreshSubscribers.forEach((callback) => callback())
refreshSubscribers.length = 0
}
function addRefreshSubscriber(callback) {
refreshSubscribers.push(callback)
}
const { isFetching, error, data } = useMyFetch('users')
事件
onFetchResponse
和 onFetchError
将分别在 fetch 请求响应和错误时触发。
const { onFetchResponse, onFetchError } = useFetch(url)
onFetchResponse((response) => {
console.log(response.status)
})
onFetchError((error) => {
console.error(error.message)
})
类型声明
显示类型声明
export interface UseFetchReturn<T> {
/**
* 表示 fetch 请求是否已完成
*/
isFinished: Readonly<ShallowRef<boolean>>
/**
* HTTP fetch 响应的 statusCode
*/
statusCode: ShallowRef<number | null>
/**
* fetch 响应的原始数据
*/
response: ShallowRef<Response | null>
/**
* 可能发生的任何 fetch 错误
*/
error: ShallowRef<any>
/**
* 成功时的 fetch 响应体,可能是 JSON 或文本
*/
data: ShallowRef<T | null>
/**
* 表示当前是否正在进行 fetch 请求
*/
isFetching: Readonly<ShallowRef<boolean>>
/**
* 表示 fetch 请求是否可以中止
*/
canAbort: ComputedRef<boolean>
/**
* 表示 fetch 请求是否已中止
*/
aborted: ShallowRef<boolean>
/**
* 中止 fetch 请求
*/
abort: Fn
/**
* 手动调用 fetch
* (默认不抛出错误)
*/
execute: (throwOnFailed?: boolean) => Promise<any>
/**
* 在 fetch 请求完成后触发
*/
onFetchResponse: EventHookOn<Response>
/**
* 在 fetch 请求错误后触发
*/
onFetchError: EventHookOn
/**
* 在 fetch 完成后触发
*/
onFetchFinally: EventHookOn
get: () => UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
post: (
payload?: MaybeRefOrGetter<unknown>,
type?: string,
) => UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
put: (
payload?: MaybeRefOrGetter<unknown>,
type?: string,
) => UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
delete: (
payload?: MaybeRefOrGetter<unknown>,
type?: string,
) => UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
patch: (
payload?: MaybeRefOrGetter<unknown>,
type?: string,
) => UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
head: (
payload?: MaybeRefOrGetter<unknown>,
type?: string,
) => UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
options: (
payload?: MaybeRefOrGetter<unknown>,
type?: string,
) => UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
json: <JSON = any>() => UseFetchReturn<JSON> &
PromiseLike<UseFetchReturn<JSON>>
text: () => UseFetchReturn<string> & PromiseLike<UseFetchReturn<string>>
blob: () => UseFetchReturn<Blob> & PromiseLike<UseFetchReturn<Blob>>
arrayBuffer: () => UseFetchReturn<ArrayBuffer> &
PromiseLike<UseFetchReturn<ArrayBuffer>>
formData: () => UseFetchReturn<FormData> &
PromiseLike<UseFetchReturn<FormData>>
}
type Combination = "overwrite" | "chain"
export interface BeforeFetchContext {
/**
* 当前请求的计算后的 URL
*/
url: string
/**
* 当前请求的请求选项
*/
options: RequestInit
/**
* 取消当前请求
*/
cancel: Fn
}
export interface AfterFetchContext<T = any> {
response: Response
data: T | null
context: BeforeFetchContext
execute: (throwOnFailed?: boolean) => Promise<any>
}
export interface OnFetchErrorContext<T = any, E = any> {
error: E
data: T | null
response: Response | null
context: BeforeFetchContext
execute: (throwOnFailed?: boolean) => Promise<any>
}
export interface UseFetchOptions {
/**
* Fetch 函数
*/
fetch?: typeof window.fetch
/**
* 当 `useFetch` 被使用时是否自动运行 fetch
*
* @default true
*/
immediate?: boolean
/**
* 当以下情况发生时是否自动重新获取:
* - 如果 URL 是一个 ref,则 URL 被更改
* - 如果 payload 是一个 ref,则 payload 被更改
*
* @default false
*/
refetch?: MaybeRefOrGetter<boolean>
/**
* 请求完成之前的初始数据
*
* @default null
*/
initialData?: any
/**
* 在多少毫秒后中止请求
* `0` 表示使用浏览器默认值
*
* @default 0
*/
timeout?: number
/**
* 允许在发生 fetch 错误时更新 `data` ref,无论是在提供的回调函数中还是在 `onFetchError` 回调中改变
*
* @default false
*/
updateDataOnError?: boolean
/**
* 在 fetch 请求被分派之前立即运行
*/
beforeFetch?: (
ctx: BeforeFetchContext,
) =>
| Promise<Partial<BeforeFetchContext> | void>
| Partial<BeforeFetchContext>
| void
/**
* 在 fetch 请求返回后立即运行。
* 在任何 2xx 响应之后运行
*/
afterFetch?: (
ctx: AfterFetchContext,
) => Promise<Partial<AfterFetchContext>> | Partial<AfterFetchContext>
/**
* 在 fetch 请求返回后立即运行。
* 在任何 4xx 和 5xx 响应之后运行
*/
onFetchError?: (
ctx: OnFetchErrorContext,
) => Promise<Partial<OnFetchErrorContext>> | Partial<OnFetchErrorContext>
}
export interface CreateFetchOptions {
/**
* 除非 URL 是绝对的,否则将添加到所有 URL 前面的基础 URL
*/
baseUrl?: MaybeRefOrGetter<string>
/**
* 确定 beforeFetch、afterFetch、onFetchError 的继承行为
* @default 'chain'
*/
combination?: Combination
/**
* useFetch 函数的默认选项
*/
options?: UseFetchOptions
/**
* fetch 请求的选项
*/
fetchOptions?: RequestInit
}
export declare function createFetch(
config?: CreateFetchOptions,
): typeof useFetch
export declare function useFetch<T>(
url: MaybeRefOrGetter<string>,
): UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
export declare function useFetch<T>(
url: MaybeRefOrGetter<string>,
useFetchOptions: UseFetchOptions,
): UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
export declare function useFetch<T>(
url: MaybeRefOrGetter<string>,
options: RequestInit,
useFetchOptions?: UseFetchOptions,
): UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
源码
贡献者
更新日志
v12.8.0
on 3/5/2025v12.7.0
on 2/15/2025v12.5.0
on 1/22/2025v12.3.0
on 1/2/202559f75
- feat(toValue): deprecate toValue
from @vueuse/shared
in favor of Vue's nativev12.1.0
on 12/22/2024v12.0.0-beta.1
on 11/21/2024v11.3.0
on 11/21/2024v10.8.0
on 2/20/2024a086e
- fix: stricter typesv10.7.0
on 12/5/2023v10.6.0
on 11/9/2023v10.4.0
on 8/25/2023v10.3.0
on 7/30/2023v10.1.1
on 5/1/2023v10.0.0-beta.4
on 4/13/20234d757
- feat(types)!: rename MaybeComputedRef
to MaybeRefOrGetter
10e98
- feat(toRef)!: rename resolveRef
to toRef
0a72b
- feat(toValue): rename resolveUnref
to toValue
v10.0.0-beta.0
on 3/14/2023