封装网络请求 useFetch & $fetch

Nuxt3封装网络请求 useFetch & $fetch
临枫541
于 2024-07-11 19:29:22 发布
阅读量1.2k
收藏 28
点赞数 19
文章标签: vue.js 前端 javascript typescript
版权
前言:

刚接触、搭建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">
    // 在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>

    <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;
}
 
/**
 * 创建请求方法
 * @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

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 示例:

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>

调用 useFetch 示例

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

结果:

————————————————

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue3.0 中,我们可以使用 axios 库来发送网络请求。为了更好地管理和封装网络请求,我们可以将 axios 的一些常用配置和方法进行封装,以便在多个组件中进行调用。 下面是一个简单的 axios 封装示例: ```javascript import axios from 'axios' const instance = axios.create({ baseURL: 'http://localhost:3000', timeout: 5000 }) instance.interceptors.request.use(config => { // 在请求发送之前做些什么 return config }, error => { // 对请求错误做些什么 return Promise.reject(error) }) instance.interceptors.response.use(response => { // 对响应数据做些什么 return response.data }, error => { // 对响应错误做些什么 return Promise.reject(error) }) export default { get(url, params) { return instance.get(url, { params }) }, post(url, data) { return instance.post(url, data) } } ``` 在上面的代码中,我们首先使用 `axios.create` 方法创建了一个 axios 实例,并对实例的一些常用配置进行了设置,如 `baseURL` 和 `timeout`。然后,我们使用 `interceptors` 对请求和响应进行拦截处理,以便在请求发送和响应返回时做一些额外的操作,如在请求发送之前添加请求头信息,或者在响应返回时对响应数据进行解析。 最后,我们将封装好的请求方法暴露出去,并使用 `instance.get` 和 `instance.post` 方法来发送 GET 和 POST 请求。 使用时,我们只需要在组件中引入封装好的请求方法,并调用即可: ```javascript import request from '@/api/request' export default { methods: { fetchData() { request.get('/api/data', { page: 1, size: 10 }).then(res => { console.log(res) }) } } } ``` 在上面的代码中,我们先引入了封装好的请求方法,并在方法中调用了 `request.get` 方法来发送 GET 请求,并传入了请求的 URL 和参数。请求成功后,我们将返回的数据打印到控制台上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值