前言:
刚接触、搭建Nuxt3项目的过程还是有点懵的,有种摸石头过河的感觉,对于网络请求这块,与之前的Vue3项目有所区别,在Vue项目通常使用axios这个库进行网络请求,但在Nuxt项目并不推荐,因为有内置 fetch 相关...接下来一起学习一下Nuxt3数据请求的点点滴滴吧~
文档:
数据获取 · 快速入门 Nuxt
关键:
- useFetch 是在组件设置函数中处理数据获取的最简单方法。
- $fetch 可以根据用户交互进行网络请求。
- useAsyncData 结合
$fetch
,提供了更精细的控制。
讲解:
useAsyncData:
- 提供了一种在SSR友好的组合式中访问异步解析数据的方式
- 注意,setup期间,这里结合了$fetch,并且设置了一个key,一个唯一的键,用于确保数据获取可以在请求中正确去重
-
| <script setup> |
| const { data, pending, error, refresh } = await useAsyncData( |
| 'mountains', |
| () => $fetch('https://api.nuxtjs.dev/mountains') |
| ) |
| </script> |
复制
- 当 CMS 或第三方提供自己的查询层时。在这种情况下,您可以使用 useAsyncData 来封装您的调用,并仍然保持组合函数提供的好处。
$fetch:
- Nuxt使用 ofetch 来全局暴露`$fetch`辅助函数,用于在Vue应用程序或API路由中进行HTTP请求
- 源码:nuxt/packages/nuxt/src/app/entry.ts at main · nuxt/nuxt · GitHub
$fetch
是在Nuxt中进行HTTP调用的首选方式,而不是为Nuxt 2设计的@nuxt/http和@nuxtjs/axios。- 比如,你的页面有给用户提供交互的(按钮),那么就可以使用 $fetch ,不然控制台会有警告,网上就有不少人是在交互的时候使用useFetch而出现问题,看下面这篇文章
- 警告:[nuxt] [useFetch] Component is already mounted, please use $fetch instead. See https://nuxt.com/docs/getting-started/data-fetching
- Nuxt 3:正确的方法 --- useFetch in Nuxt 3: The Proper Way (alex.party)
- 请观察以下调用接口的时机:setup | click
-
| <script setup lang="ts"> |
| |
| const dataTwice = await $fetch('/api/item') |
| |
| |
| const { data } = await useAsyncData('item', () => $fetch('/api/item')) |
| |
| |
| const { data } = await useFetch('/api/item') |
| </script> |
复制
| <script setup lang="ts"> |
| function contactForm() { |
| $fetch('/api/contact', { |
| method: 'POST', |
| body: { hello: 'world '} |
| }) |
| } |
| </script> |
| |
| <template> |
| <button @click="contactForm">联系我们</button> |
| </template> |
复制
useFetch :
- 使用一个与SSR兼容的可组合函数从API端点获取数据。
- 包装了useAsyncData和$fetch,它返回响应式的可组合函数,并处理将响应添加到Nuxt的负载中,以便在页面水合时可以从服务器传递给客户端,而无需在客户端重新获取数据。
- (水合的概念在文档的渲染模式有讲解:渲染模式 · 关键概念 (nuxt.com.cn))
- 提供了拦截器
-
| const { data, pending, error, refresh } = await useFetch('/api/auth/login', { |
| onRequest({ request, options }) { |
| |
| options.headers = options.headers || {} |
| options.headers.authorization = '...' |
| }, |
| onRequestError({ request, options, error }) { |
| |
| }, |
| onResponse({ request, response, options }) { |
| |
| localStorage.setItem('token', response._data.token) |
| }, |
| onResponseError({ request, response, options }) { |
| |
| } |
| }) |
复制
- 事实上,
useFetch(url)
几乎等同于 useAsyncData(url, () => $fetch(url))
- 它是为最常见的用例提供的开发者体验糖。
封装:工厂函数设计请求代码结构
env:
- 在nuxt.config.ts配置 runtimeConfig,通过useRuntimeConfig()解构,示例:
| export default defineNuxtConfig({ |
| runtimeConfig: { |
| public: { |
| API_BASE_DEV: 'http://localhost:4000', |
| API_BASE_PROD: 'https://api.example.com/v1' |
| } |
| }, |
| }) |
复制

composables:
-
封装$fetch
- - composables/useDollarFetchRequest.ts
| import { $fetch } from 'ofetch'; |
| import { useRuntimeConfig } from '#app'; |
| |
| interface RequestOptions { |
| customBaseURL?: string; |
| [key: string]: any; |
| } |
| |
| type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'; |
| |
| |
| function handleRequest(options: RequestOptions) { |
| options.headers = { |
| ...options.headers, |
| 'Content-Type': 'application/json', |
| }; |
| } |
| |
| |
| function handleResponse(response: any) { |
| if (response.error) { |
| throw new Error(response.error.message || '响应错误'); |
| } |
| return response; |
| } |
| |
| |
| |
| |
| |
| function createDollarFetchRequest(method: HttpMethod) { |
| return async function ( |
| url: string, |
| data?: any, |
| options: RequestOptions = {} |
| ) { |
| const { |
| public: { |
| API_BASE_DEV, |
| API_BASE_PROD |
| } |
| } = useRuntimeConfig(); |
| |
| const baseURL = process.env.NODE_ENV === 'production' |
| ? API_BASE_PROD |
| : API_BASE_DEV; |
| |
| const requestUrl = new URL( |
| url, |
| options.customBaseURL || baseURL |
| ).toString(); |
| |
| try { |
| handleRequest(options); |
| const response = await $fetch(requestUrl, { |
| method, |
| body: data, |
| ...options, |
| }); |
| return handleResponse(response); |
| } catch (error) { |
| console.error('请求错误:', error); |
| throw error; |
| } |
| }; |
| } |
| |
| |
| export const useDollarGet = createDollarFetchRequest('GET'); |
| export const useDollarPost = createDollarFetchRequest('POST'); |
| export const useDollarPut = createDollarFetchRequest('PUT'); |
| export const useDollarDelete = createDollarFetchRequest('DELETE'); |
复制
-
封装useFetch
- - composables/useFetchRequest.ts
| import { useFetch, useRuntimeConfig } from '#app'; |
| import type { UseFetchOptions } from 'nuxt/app'; |
| |
| interface RequestOptions extends UseFetchOptions<any> { |
| customBaseURL?: string; |
| } |
| |
| type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'; |
| type HandleRequestOptions = { request: Request; options: RequestOptions }; |
| type HandleResponseOptions = { response: any }; |
| |
| |
| function handleRequest({ options }: HandleRequestOptions) { |
| options.headers = { |
| ...options.headers, |
| 'Content-Type': 'application/json', |
| }; |
| } |
| |
| |
| function handleResponse({ response }: HandleResponseOptions) { |
| if (response._data.error) { |
| throw new Error(response._data.error.message || '响应错误'); |
| } |
| return response._data; |
| } |
| |
| |
| |
| |
| |
| function createUseFetchRequest(method: HttpMethod) { |
| return async function ( |
| url: string, |
| data?: any, |
| options: RequestOptions = {} |
| ) { |
| const { |
| public: { |
| API_BASE_DEV, |
| API_BASE_PROD |
| } |
| } = useRuntimeConfig(); |
| |
| const baseURL = process.env.NODE_ENV === 'production' |
| ? API_BASE_PROD |
| : API_BASE_DEV; |
| |
| const requestUrl = new URL( |
| url, |
| options.customBaseURL || baseURL |
| ).toString(); |
| |
| return await useFetch(requestUrl, { |
| ...options, |
| method, |
| body: data, |
| onRequest: handleRequest, |
| onResponse: handleResponse |
| }); |
| }; |
| } |
| |
| |
| export const useFetchGet = createUseFetchRequest('GET'); |
| export const useFetchPost = createUseFetchRequest('POST'); |
| export const useFetchPut = createUseFetchRequest('PUT'); |
| export const useFetchDelete = createUseFetchRequest('DELETE'); |
复制
统一管理 API
| import { useDollarGet } from '~/composables/useDollarFetchRequest'; |
| |
| export const getDocsApi = () => useDollarGet('/docs/list'); |
复制
| <template> |
| <div> |
| <button @click="handleGetUserInfo">获取用户信息</button> |
| </div> |
| |
| <HomeCover /> |
| <HomeIntro /> |
| <HomeCadre /> |
| <HomeJoinUs /> |
| <BackToTop /> |
| </template> |
| |
| <script setup lang="ts"> |
| import HomeCover from './HomeCover.vue'; |
| import HomeIntro from './HomeIntro.vue'; |
| import HomeCadre from './HomeCadre.vue'; |
| import HomeJoinUs from './HomeJoinUs.vue'; |
| import { getDocsApi } from '../../api/home/joinUs'; |
| |
| const handleGetUserInfo = async () => { |
| try { |
| const data = await getDocsApi(); |
| console.log('文档列表:', data); |
| } catch (error) { |
| console.error('获取文档列表出错:', error); |
| } |
| }; |
| </script> |
复制
| <script setup lang="ts"> |
| import { getDocsApi } from '../../api/home/joinUs'; |
| |
| try { |
| const response = await getDocsApi(); |
| console.log('文档列表:', response.data.value); |
| } catch (error) { |
| console.error('获取文档列表出错:', error); |
| } |
| |
| </script> |
复制
结果:

欢迎三连,以及在评论区讨论,如果有不对的还请告知纠正