跳到主要內容

useFetch

分類
匯出大小
2.24 kB
上次變更
5 天前

響應式 Fetch API 提供了中止請求、在請求發送前攔截請求、在 URL 變更時自動重新獲取請求,以及使用預定義選項建立您自己的 useFetch 的能力。

透過 Vue School 的免費影片課程學習 useFetch!

提示

當與 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' // Will trigger another request

防止請求立即發送

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 屬性自動中止請求。當達到給定的 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' // Modifies the response data

    return ctx
  },
})

updateDataOnError 設定為 true 時,onFetchError 選項可以在回應資料和錯誤更新之前攔截它們。

ts
const { data } = useFetch(url, {
  updateDataOnError: true,
  onFetchError(ctx) {
    // ctx.data can be null when 5xx response
    if (ctx.data === null)
      ctx.data = { title: 'Hunter x Hunter' } // Modifies the response data

    ctx.error = new Error('Custom Error') // Modifies the error
    return ctx
  },
})

console.log(data.value) // { title: 'Hunter x Hunter' }

設定請求方法和回傳類型

可以透過將適當的方法添加到 useFetch 的末尾來設定請求方法和回傳類型

ts
// Request will be sent with GET method and data will be parsed as JSON
const { data } = useFetch(url).get().json()

// Request will be sent with POST method and data will be parsed as text
const { data } = useFetch(url).post().text()

// Or set the method using the options

// Request will be sent with GET method and data will be parsed as 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 in pre-configured instance will only run when the newly spawned instance do not pass beforeFetch
    async beforeFetch({ options }) {
      const myToken = await getMyToken()
      options.headers.Authorization = `Bearer ${myToken}`

      return { options }
    },
  },
})

// use useMyFetch beforeFetch
const { isFetching, error, data } = useMyFetch('users')

// use custom 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
              // handle refresh token error
            }
          })
        }

        return new Promise((resolve) => {
          addRefreshSubscriber(() => {
            execute().then((response) => {
              resolve({ data, response })
            })
          })
        })
      }

      return { data, response }
    },
    // or use onFetchError with updateDataOnError
    updateDataOnError: true,
    onFetchError({ error, data, response, context, execute }) {
      // same as 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
              // handle refresh token error
            }
          })
        }
        return new Promise((resolve) => {
          addRefreshSubscriber(() => {
            execute().then((response) => {
              resolve({ data, response })
            })
          })
        })
      }
      return { data, response }
    },
    // or use onFetchError with updateDataOnError
    updateDataOnError: true,
    onFetchError({ error, data, response, context, execute }) {
      // same as 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> {
  /**
   * Indicates if the fetch request has finished
   */
  isFinished: Readonly<ShallowRef<boolean>>
  /**
   * The statusCode of the HTTP fetch response
   */
  statusCode: ShallowRef<number | null>
  /**
   * The raw response of the fetch response
   */
  response: ShallowRef<Response | null>
  /**
   * Any fetch errors that may have occurred
   */
  error: ShallowRef<any>
  /**
   * The fetch response body on success, may either be JSON or text
   */
  data: ShallowRef<T | null>
  /**
   * Indicates if the request is currently being fetched.
   */
  isFetching: Readonly<ShallowRef<boolean>>
  /**
   * Indicates if the fetch request is able to be aborted
   */
  canAbort: ComputedRef<boolean>
  /**
   * Indicates if the fetch request was aborted
   */
  aborted: ShallowRef<boolean>
  /**
   * Abort the fetch request
   */
  abort: Fn
  /**
   * Manually call the fetch
   * (default not throwing error)
   */
  execute: (throwOnFailed?: boolean) => Promise<any>
  /**
   * Fires after the fetch request has finished
   */
  onFetchResponse: EventHookOn<Response>
  /**
   * Fires after a fetch request error
   */
  onFetchError: EventHookOn
  /**
   * Fires after a fetch has completed
   */
  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 {
  /**
   * The computed url of the current request
   */
  url: string
  /**
   * The request options of the current request
   */
  options: RequestInit
  /**
   * Cancels the current request
   */
  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 function
   */
  fetch?: typeof window.fetch
  /**
   * Will automatically run fetch when `useFetch` is used
   *
   * @default true
   */
  immediate?: boolean
  /**
   * Will automatically refetch when:
   * - the URL is changed if the URL is a ref
   * - the payload is changed if the payload is a ref
   *
   * @default false
   */
  refetch?: MaybeRefOrGetter<boolean>
  /**
   * Initial data before the request finished
   *
   * @default null
   */
  initialData?: any
  /**
   * Timeout for abort request after number of millisecond
   * `0` means use browser default
   *
   * @default 0
   */
  timeout?: number
  /**
   * Allow update the `data` ref when fetch error whenever provided, or mutated in the `onFetchError` callback
   *
   * @default false
   */
  updateDataOnError?: boolean
  /**
   * Will run immediately before the fetch request is dispatched
   */
  beforeFetch?: (
    ctx: BeforeFetchContext,
  ) =>
    | Promise<Partial<BeforeFetchContext> | void>
    | Partial<BeforeFetchContext>
    | void
  /**
   * Will run immediately after the fetch request is returned.
   * Runs after any 2xx response
   */
  afterFetch?: (
    ctx: AfterFetchContext,
  ) => Promise<Partial<AfterFetchContext>> | Partial<AfterFetchContext>
  /**
   * Will run immediately after the fetch request is returned.
   * Runs after any 4xx and 5xx response
   */
  onFetchError?: (
    ctx: OnFetchErrorContext,
  ) => Promise<Partial<OnFetchErrorContext>> | Partial<OnFetchErrorContext>
}
export interface CreateFetchOptions {
  /**
   * The base URL that will be prefixed to all urls unless urls are absolute
   */
  baseUrl?: MaybeRefOrGetter<string>
  /**
   * Determine the inherit behavior for beforeFetch, afterFetch, onFetchError
   * @default 'chain'
   */
  combination?: Combination
  /**
   * Default Options for the useFetch function
   */
  options?: UseFetchOptions
  /**
   * Options for the fetch request
   */
  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>>

來源

SourceDemoDocs

貢獻者

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 於 2025/3/5
7432f - feat(types): 棄用 MaybeRefMaybeRefOrGetter,改用 Vue 原生型別 (#4636)
v12.7.0 於 2025/2/15
3ca0d - fix: {combination: 'overwrite'} 時的部分覆寫問題 (#4430)
v12.5.0 於 2025/1/22
98a83 - feat: 新增參數到 afterFetchonFetchError (#4499)
v12.3.0 於 2025/1/2
59f75 - feat(toValue): 棄用 @vueuse/shared 中的 toValue,改用 Vue 原生型別
v12.1.0 於 2024/12/22
8a89d - fix: 處理空 payload (#4366)
v12.0.0-beta.1 於 2024/11/21
0a9ed - feat!: 移除 Vue 2 支援,優化 bundle 並清理程式碼 (#4349)
v11.3.0 於 2024/11/21
3d29c - feat: 推斷陣列 payload 的 'json' 類型 (#4329)
3de68 - fix: 確保單斜線 (#4296)
v10.8.0 於 2024/2/20
f5587 - fix: 移除 iterable 轉換中不必要的展開運算符 (#3660)
31d4a - fix: 將 isFinished, isFetching 標記為 readonly (#3616)
a086e - fix: 更嚴格的類型
v10.7.0 於 2023/12/5
fccf2 - feat: 升級 deps (#3614)
8cbfd - fix: 讀取時複製 'Response' (#3607) (#3608)
3456d - fix: 在請求完成後立即修改狀態 (#3603)
v10.6.0 於 2023/11/9
75ca2 - fix: 當請求因重新獲取而中止時,不要將 isFetching 設定為 false (#3479)
v10.4.0 於 2023/8/25
945ca - feat: 引入 updateDataOnError 選項 (#3092)
v10.3.0 於 2023/7/30
b7e3d - fix: 在 execute 上生成 payloadType (#3251)
v10.1.1 於 2023/5/1
d051f - fix: combineCallbacks 不會合併選項 (#3015)
v10.0.0-beta.4 於 2023/4/13
4d757 - feat(types)!: 將 MaybeComputedRef 重命名為 MaybeRefOrGetter
10e98 - feat(toRef)!: 將 resolveRef 重命名為 toRef
0a72b - feat(toValue): 將 resolveUnref 重命名為 toValue
v10.0.0-beta.0 於 2023/3/14
78cfb - feat: 成功時更新資料 (#2711)
fff45 - fix: 在更新狀態之前中止 (#2805)
v9.13.0 於 2023/2/18
cd9d6 - feat: 重新獲取時取消先前的請求 (#2750)
c2bc6 - fix: 檢查 isFetchOptions 是否為物件

根據 MIT 許可證發布。