基于Nuxt3 useFetch 的 HTTP 请求封装

802 阅读1分钟

前言

在 Nuxt3 项目开发中,我们经常需要处理大量的 HTTP 请求。为了提高开发效率、优化性能并保持代码的可维护性,一个好的请求封装是非常必要的。本文将分享一个基于 Nuxt3 的 HTTP 请求封装方案。

需求分析

在开始封装之前,让我们先明确需求:

  1. 统一的请求配置和错误处理
  2. GET 请求缓存支持
  3. 类型安全
  4. 易于使用和维护
  5. 符合 Nuxt3 的开发理念
import type { UseFetchOptions } from "nuxt/app";

type Methods = "GET" | "POST" | "DELETE" | "PUT";

interface CacheItem {
    data: any;
    timestamp: number;
}


const BASE_URL = "";

export interface IResultData<T> {
    code: number;
    data: T;
    msg: string;
}

class HttpRequest {
    // 最大缓存条数
    private readonly MAX_CACHE_SIZE = 50;
    // 缓存过期时间 5分钟
    private readonly CACHE_EXPIRE_TIME = 5 * 60 * 1000;

    private getCache() {
        return useState('http-cache', () => new Map<string, CacheItem>());
    }

    request<T = any>(
        url: string,
        method: Methods,
        data: any,
        options?: UseFetchOptions<T>,
    ) {
        const{$i18n} = useNuxtApp()
        return new Promise((resolve, reject) => {
            // 生成缓存键
            const cacheKey = method === 'GET' ? `${url}-${JSON.stringify(data)}` : '';
            const cache = this.getCache();

            // 如果是 GET 请求且存在缓存,检查是否过期
            if (method === 'GET' && cache.value.has(cacheKey)) {
                const cachedItem = cache.value.get(cacheKey)!;
                if (Date.now() - cachedItem.timestamp < this.CACHE_EXPIRE_TIME) {
                    return resolve({ data: ref(cachedItem.data) });
                }
                // 如果缓存过期,直接删除
                cache.value.delete(cacheKey);
            }

            const newOptions: UseFetchOptions<T> = {
                baseURL: BASE_URL,
                method: method,
                headers:{
                    'tenant-id':'164',
                    'Accept-Language':$i18n.locale || 'zh'
                },
                ...options,
            };

            if (method === "GET" || method === "DELETE") {
                newOptions.params = data;
            }
            if (method === "POST" || method === "PUT") {
                newOptions.body = data;
            }

            useFetch(url, newOptions)
                .then((res) => {
                    // 如果是 GET 请求,缓存结果
                    if (method === 'GET' && res.data.value) {
                        // 如果缓存达到上限,删除最旧的一条数据
                        if (cache.value.size >= this.MAX_CACHE_SIZE) {
                            const oldestKey = Array.from(cache.value.keys())[0];
                            cache.value.delete(oldestKey);
                        }

                        // 直接设置新的缓存数据
                        cache.value.set(cacheKey, {
                            data: res.data.value,
                            timestamp: Date.now()
                        });
                    }
                    resolve(res);
                })
                .catch((error) => {
                    reject(error);
                });
        });
    }

    // 封装常用方法

    get<T = any>(url: string, params?: any, options?: UseFetchOptions<T>) {
        return this.request(url, "GET", params, options);
    }

    post<T = any>(url: string, data: any, options?: UseFetchOptions<T>) {
        return this.request(url, "POST", data, options);
    }

    Put<T = any>(url: string, data: any, options?: UseFetchOptions<T>) {
        return this.request(url, "PUT", data, options);
    }

    Delete<T = any>(url: string, params: any, options?: UseFetchOptions<T>) {
        return this.request(url, "DELETE", params, options);
    }

    // 清除缓存的方法
    clearCache() {
        const cache = this.getCache();
        cache.value.clear();
    }
}

const httpRequest = new HttpRequest();

export default httpRequest;

基础使用

// GET 请求示例
const response = await httpRequest.get('/api/data', { id: 1 });

// POST 请求示例
const response = await httpRequest.post('/api/data', { name: 'test' });

可以自行使用lru策略来优化