前言:
刚接触、搭建Nuxt3项目的过程还是有点懵的,有种摸石头过河的感觉,对于网络请求这块,与之前的Vue3项目有所区别,在Vue项目通常使用axios这个库进行网络请求,但在Nuxt项目并不推荐,因为有内置 fetch 相关...接下来一起学习一下Nuxt3数据请求的点点滴滴吧~
文档:
关键:
- useFetch 是在组件设置函数中处理数据获取的最简单方法。
- [fetch](https://nuxt.com.cn/docs/api/utils/dollarfetch "fetch") 可以根据用户交互进行网络请求。
- useAsyncData 结合
$fetch
,提供了更精细的控制。
讲解:
useAsyncData:
- 提供了一种在SSR友好的组合式中访问异步解析数据的方式
- 注意,setup期间,这里结合了$fetch,并且设置了一个key,一个唯一的键,用于确保数据获取可以在请求中正确去重
javascript
<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
javascript
<script setup lang="ts">
// 在SSR中数据将被获取两次,一次在服务器端,一次在客户端。
const dataTwice = await $fetch('/api/item')
// 在SSR中,数据仅在服务器端获取并传递到客户端。
const { data } = await useAsyncData('item', () => $fetch('/api/item'))
// 你也可以使用useFetch作为useAsyncData + $fetch的快捷方式
const { data } = await useFetch('/api/item')
</script>
javascript
<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](https://nuxt.com.cn/docs/api/utils/dollarfetch "fetch"),它返回响应式的可组合函数,并处理将响应添加到Nuxt的负载中,以便在页面水合时可以从服务器传递给客户端,而无需在客户端重新获取数据。
- (水合的概念在文档的渲染模式有讲解:渲染模式 · 关键概念 (nuxt.com.cn))
- 提供了拦截器
javascript
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()解构,示例:
javascript
export default defineNuxtConfig({
runtimeConfig: {
public: {
API_BASE_DEV: 'http://localhost:4000',
API_BASE_PROD: 'https://api.example.com/v1'
}
},
})
- 当然你还可以
composables:
封装$fetch
-
- composables/useDollarFetchRequest.ts
TypeScript
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;
}
/**
* 创建请求方法
* @param method
*/
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;
}
};
}
// 提供 $fetch & HTTP 方法 - 统一管理请求 - 再到组件中使用
export const useDollarGet = createDollarFetchRequest('GET');
export const useDollarPost = createDollarFetchRequest('POST');
export const useDollarPut = createDollarFetchRequest('PUT');
export const useDollarDelete = createDollarFetchRequest('DELETE');
封装useFetch
-
- composables/useFetchRequest.ts
TypeScript
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;
}
/**
* 创建请求方法
* @param method
*/
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
});
};
}
// 提供 useFetch & HTTP 方法 - 统一管理请求 - 再到组件中使用
export const useFetchGet = createUseFetchRequest('GET');
export const useFetchPost = createUseFetchRequest('POST');
export const useFetchPut = createUseFetchRequest('PUT');
export const useFetchDelete = createUseFetchRequest('DELETE');
统一管理 API
- 调用 $fetch 示例:
javascript
import { useDollarGet } from '~/composables/useDollarFetchRequest';
export const getDocsApi = () => useDollarGet('/docs/list');
javascript
<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>
- 调用 useFetch 示例
TypeScript
<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>
结果:
欢迎三连,以及在评论区讨论,如果有不对的还请告知纠正