Nuxt 中的自定义 useFetch

如何在 Nuxt 3 中为调用外部 API 创建一个自定义的 fetcher。

在使用Nuxt时,你可能需要在客户端请求外部API,并希望为从你的API获取设置一些默认选项。

$fetch(由 useFetch 使用)辅助函数故意没有全局配置。这是为了确保应用程序中的获取行为保持一致,并且其他集成(如模块)可以依赖核心工具(如 $fetch)的行为。

然而,Nuxt 提供了一种方法来为你的 API(或者如果你有多个 API 需要调用,则为多个 fetcher)创建一个自定义 fetcher。

自定义 $fetch

让我们使用一个 Nuxt 插件创建一个自定义的 $fetch 实例。

$fetch 是一个配置好的 ofetch 实例,它支持将 Nuxt 服务器的基本 URL 作为参数,以及在 SSR 期间直接函数调用(避免 HTTP 往返)。

让我们假设:

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) {
        // note that this relies on ofetch >= 1.4.0 - you may need to refresh your lockfile
        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 插件,$apiuseNuxtApp() 暴露出来,可以直接从 Vue 组件中调用 API:

app.vue
<script setup>
const { $api } = useNuxtApp()
const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
</script>
使用useAsyncData包装时避免在服务器端渲染时重复获取数据(服务器和客户端在 hydration 时)。

自定义 useFetch/useAsyncData

既然 $api 有了我们想要的逻辑,让我们创建一个 useAPI 组合器来取代 useAsyncData + $api 的使用:

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

让我们使用新的组合器和一个干净的组件:

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

If you want to customize the type of any error returned, you can also do so:

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

interface CustomError {
  message: string
  statusCode: number
}

export function useAPI<T>(
  url: string | (() => string),
  options?: UseFetchOptions<T>,
) {
  return useFetch<T, FetchError<CustomError>>(url, {
    ...options,
    $fetch: useNuxtApp().$api
  })
}
这个例子演示了如何使用自定义的 useFetch,但同样的结构对于自定义 useAsyncData 是相同的。
观看有关 Nuxt 中自定义 $fetch 和存储库模式的视频。
我们正在讨论找到一个更清洁的方法来让你创建一个自定义 fetcher,请查看 https://github.com/nuxt/nuxt/issues/14736。