Skip to content

useFetch

类别
导出体积
2.24 kB
上次更改
5 days ago

响应式 Fetch API 提供了中止请求、在请求发送前拦截请求、当 URL 变化时自动重新请求、以及创建带有预定义选项的 useFetch 功能的能力。

通过 Vue School 的这个免费 视频课程学习 useFetch!

TIP

在使用 Nuxt 3 时,此功能将 不会 自动导入,而是使用 Nuxt 内置的 useFetch()。如果您想使用 VueUse 中的函数,请使用显式导入。

示例

以下URL可用于测试useFetch的不同功能
普通请求: 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 可以是一个字符串或一个 refdata 对象将包含请求的结果,error 对象将包含任何错误,而 isFetching 对象将指示请求是否正在加载。

ts
import { useFetch } from '@vueuse/core'

const { isFetching, error, data } = useFetch(url)

异步用法

useFetch 也可以像普通的 fetch 一样被 await。请注意,任何异步组件都必须将使用它的组件包裹在 <Suspense> 标签中。您可以在 官方 Vue 3 文档 中了解更多关于 suspense API 的信息。

ts
import { useFetch } from '@vueuse/core'

const { isFetching, error, data } = await useFetch(url)

URL 变化时重新请求

使用 ref 作为 URL 参数将允许 useFetch 函数在 URL 更改时自动触发另一个请求。

ts
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 函数之前发送。

ts
const { execute } = useFetch(url, { immediate: false })

execute()

中止请求

可以使用 useFetch 函数中的 abort 函数中止请求。canAbort 属性指示请求是否可以中止。

ts
const { abort, canAbort } = useFetch(url)

setTimeout(() => {
  if (canAbort.value)
    abort()
}, 100)

也可以通过使用 timeout 属性自动中止请求。当达到给定的超时时间时,它将调用 abort 函数。

ts
const { data } = useFetch(url, { timeout: 100 })

拦截请求

beforeFetch 选项可以在请求发送之前拦截请求并修改请求选项和 URL。

ts
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 选项可以在响应数据更新之前拦截响应数据。

ts
const { data } = useFetch(url, {
  afterFetch(ctx) {
    if (ctx.data.title === 'HxH')
      ctx.data.title = 'Hunter x Hunter' // 修改响应数据

    return ctx
  },
})

onFetchError 选项可以在将响应数据和错误更新之前拦截它们,前提是 updateDataOnError 设置为 true

ts
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 的末尾添加适当的方法可以设置请求方法和返回类型。

ts
// 请求将使用 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 进行交互非常有用。

ts
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')

如果您想在预配置实例和新生成实例之间控制 beforeFetchafterFetchonFetchError 的行为,您可以提供一个 combination 选项以切换 overwritechaining

ts
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,
    }
  },
})

您可以通过在 afterFetchonFetchError 中调用 execute 方法来重新执行请求。以下是刷新令牌的简单示例:

ts
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')
js
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')

事件

onFetchResponseonFetchError 将分别在 fetch 请求响应和错误时触发。

ts
const { onFetchResponse, onFetchError } = useFetch(url)

onFetchResponse((response) => {
  console.log(response.status)
})

onFetchError((error) => {
  console.error(error.message)
})

类型声明

显示类型声明
typescript
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>>

源码

源码演示文档

贡献者

Anthony Fu
一纸忘忧
wheat
Anthony Fu
Jelf
Ismail Gjevori
IlyaL
qiang
Robin
丶远方
青椒肉丝
KaKa
Toby Zerner
Jay214
webfansplz
James Garbutt
Robin
Gergely Dremák
mrchar
Pouya Mohammadkhani
BaboonKing
LJFloor
mymx2
Arthur Machado
Martijn Weghorst
KaKa
RAX7
Przemek Brzosko
abitwhy
sun0day
Young
sun0day
Curt Grimes
Yvan Zhu
ice
Antonio Román
Glandos
unknown_
btea
Shinigami
KaKa
Arda Soytürk

更新日志

v12.8.0 on 3/5/2025
7432f - feat(types): deprecate MaybeRef and MaybeRefOrGetter in favor of Vue's native (#4636)
v12.7.0 on 2/15/2025
3ca0d - fix: partial overwrite when {combination: 'overwrite'} (#4430)
v12.5.0 on 1/22/2025
98a83 - feat: add parameters to the afterFetch and onFetchError (#4499)
v12.3.0 on 1/2/2025
59f75 - feat(toValue): deprecate toValue from @vueuse/shared in favor of Vue's native
v12.1.0 on 12/22/2024
8a89d - fix: handle empty payload (#4366)
v12.0.0-beta.1 on 11/21/2024
0a9ed - feat!: drop Vue 2 support, optimize bundles and clean up (#4349)
v11.3.0 on 11/21/2024
3d29c - feat: infer 'json' type for array payloads (#4329)
3de68 - fix: ensure single slash (#4296)
v10.8.0 on 2/20/2024
f5587 - fix: remove unnecessary spread operator in iterable conversion (#3660)
31d4a - fix: mark isFinished, isFetching readonly (#3616)
a086e - fix: stricter types
v10.7.0 on 12/5/2023
fccf2 - feat: upgrade deps (#3614)
8cbfd - fix: clone 'Response' on reading (#3607) (#3608)
3456d - fix: immediately modify the status after the request is completed (#3603)
v10.6.0 on 11/9/2023
75ca2 - fix: don't set isFetching to false when a request is aborted because of a refetch (#3479)
v10.4.0 on 8/25/2023
945ca - feat: introduce updateDataOnError option (#3092)
v10.3.0 on 7/30/2023
b7e3d - fix: generated payloadType on execute (#3251)
v10.1.1 on 5/1/2023
d051f - fix: combineCallbacks does not merge options (#3015)
v10.0.0-beta.4 on 4/13/2023
4d757 - 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
78cfb - feat: update data on success (#2711)
fff45 - fix: abort before updating state (#2805)