---
title: "Nuxt 中的自定义 useFetch"
description: "如何在 Nuxt 中创建调用外部 API 的自定义 fetcher。"
canonical_url: "https://nuxt.zhcndoc.com/docs/3.x/guide/recipes/custom-usefetch"
---
# Nuxt 中的自定义 useFetch

> 如何在 Nuxt 中创建调用外部 API 的自定义 fetcher。

使用 Nuxt 时，你可能正在构建前端并调用外部 API，或者想为 API 调用设置一些默认的 fetch 选项。

[`$fetch`](https://nuxt.zhcndoc.com/docs/3.x/api/utils/dollarfetch) 工具函数（由 [`useFetch`](https://nuxt.zhcndoc.com/docs/3.x/api/composables/use-fetch) 组合式函数使用）有意不支持全局配置。这一点很重要，这样可以确保应用中的 fetch 行为保持一致，并且其他集成（如模块）可以依赖 `$fetch` 等核心工具的行为。

不过，Nuxt 提供了一种方法，允许你为 API 创建自定义 fetcher（如果有多个 API 需要调用，也可以创建多个 fetcher）。

## 自定义 `$fetch`

让我们使用 [Nuxt 插件](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/plugins)创建一个自定义的 `$fetch` 实例。

::note
`$fetch` 是配置过的 [ofetch](https://github.com/unjs/ofetch) 实例，支持自动添加你的 Nuxt 服务器的基础 URL，并且在 SSR 阶段直接进行函数调用，从而避免 HTTP 网络往返。
::

假设：

- 主 API 是 <https://api.nuxt.com>
- 我们使用 [nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils) 在 session 中存储 JWT token
- 如果 API 返回 `401` 状态码，我们就将用户重定向到 `/login` 页面

```ts [plugins/api.ts]
export default defineNuxtPlugin((nuxtApp) => {
  const { session } = useUserSession()

  const api = $fetch.create({
    baseURL: 'https://api.nuxt.com',
    onRequest ({ request, options, error }) {
      if (session.value?.token) {
        // 注意：这依赖于 ofetch >= 1.4.0 - 你可能需要刷新锁文件
        options.headers.set('Authorization', `Bearer ${session.value?.token}`)
      }
    },
    async onResponseError ({ response }) {
      if (response.status === 401) {
        await nuxtApp.runWithContext(() => navigateTo('/login'))
      }
    },
  })

  // 通过 useNuxtApp().$api 暴露
  return {
    provide: {
      api,
    },
  }
})
```

使用这个 Nuxt 插件后，`$api` 会在 `useNuxtApp()` 中暴露，方便你直接在 Vue 组件里调用 API：

```vue [app.vue]
<script setup>
const { $api } = useNuxtApp()
const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
</script>
```

::callout
使用 [`useAsyncData`](https://nuxt.zhcndoc.com/docs/3.x/api/composables/use-async-data) 包装，可以**避免在服务端渲染期间重复获取数据**（服务器端和客户端 hydration 时）。
::

## 自定义 `useFetch`/`useAsyncData`

既然 `$api` 已包含我们期望的逻辑，让我们构建一个 `useAPI` 组合式函数，用来替代原本 `useAsyncData` + `$api` 的用法：

```ts [composables/useAPI.ts]
import type { UseFetchOptions } from 'nuxt/app'

export function useAPI<T> (
  url: string | (() => string),
  options?: UseFetchOptions<T>,
) {
  return useFetch(url, {
    ...options,
    $fetch: useNuxtApp().$api as typeof $fetch,
  })
}
```

现在我们可以使用这个新的组合式函数，编写更加整洁的组件：

```vue [app.vue]
<script setup>
const { data: modules } = await useAPI('/modules')
</script>
```

如果你想自定义返回错误的类型，也可以这样做：

```ts
import type { FetchError } from 'ofetch'
import type { UseFetchOptions } from 'nuxt/app'

interface CustomError {
  message: string
  status: number
}

export function useAPI<T> (
  url: string | (() => string),
  options?: UseFetchOptions<T>,
) {
  return useFetch<T, FetchError<CustomError>>(url, {
    ...options,
    $fetch: useNuxtApp().$api,
  })
}
```

::note
此示例展示了如何使用自定义的 `useFetch`，自定义 `useAsyncData` 的结构完全相同。
::

::link-example{to="https://nuxt.zhcndoc.com/docs/examples/advanced/use-custom-fetch-composable"}
::

::video-accordion{title="观看有关 Nuxt 中自定义 $fetch 及 Repository 模式的视频" videoId="jXH8Tr-exhI"}
::

::note
我们目前正在讨论一种更简洁的方式，让你能够创建自定义 fetcher，详见：<https://github.com/nuxt/nuxt/issues/14736>。
::


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
