Nuxt3最新2024封装请求+代理转发完整版

369 阅读2分钟

Nuxt3通用请求

图片

新建utils目录

1.文件request.ts

// 文件目录:service/index.ts

import { UseFetchOptions } from "nuxt/app";
import { useNuxtApp } from "#app";

// HTTP 请求的方法类型
type Methods = "GET" | "POST" | "DELETE" | "PUT";

// URL 基地址
const BASE_URL = "/api/";

// 请求结果数据格式
export interface IResultData<T> {
    code: number;
    data: T;
    msg: string;
}

/**
 * api请求封装,使用useFetch函数或$fetch函数
 * @param { string } url 请求地址
 * @param { string } method 请求方法
 * @param { object } data 请求数据
 * @param { UseFetchOptions } options 请求选项
 */
class HttpRequest {
    request<T = any>(url: string, method: Methods, data: any, options?: UseFetchOptions<T>) {
        return new Promise((resolve, reject) => {
            // 继承UseFetchOptions类型,包含了baseURL和method两个属性
            const newOptions: UseFetchOptions<T> = {
                baseURL: BASE_URL,
                method,
                ...options,
            };
            const token = useCookie('token');
            if (token.value) {
                newOptions.headers = {
                    Authorization: `Bearer ${token.value}`
                };
            }

            // 根据请求方法处理请求的数据
            if (method === "GET" || method === "DELETE") {
                // 将数据设置为newOptions的params属性
                newOptions.params = data;
            }
            if (method === "POST" || method === "PUT") {
                // 将数据设置为newOptions的body属性
                newOptions.body = data;
            }

            const nuxtApp = useNuxtApp();

            // 选择合适的请求方法
            const fetchMethod = (process.client && !nuxtApp.isHydrating) ? nuxtApp.$request : useFetch as any;

            // 发送请求
            fetchMethod(url, newOptions)
                .then((res: any) => {
                    resolve(res.status? res.data.value:res);
                })
                .catch((error: any)  => {
                    let errorMessage = "服务端错误";

                    if (error.response && error.response._data) {
                        let data = error.response._data;
                        if (typeof error.response._data === 'string') {
                            try {
                                data = JSON.parse(error.response._data);
                            } catch (e) {
                                errorMessage = error.response._data;
                            }
                        }
                        if (data.errors) {
                            const errorMessages = [];
                            for (const key in data.errors) {
                                errorMessages.push(`${data.errors[key].join(', ')}`);
                            }
                            errorMessage = errorMessages.join('; ') || errorMessage;
                        } else {
                            errorMessage = data.message || errorMessage;
                        }
                    }
                    if (process.client) {
                        Message.error(errorMessage);
                    }
                    if (error.response &&
                        (error.response._data.code === 40001 || error.response._data.code === 40002 || error.response._data.code === 40003)) {
                        resolve(error.response._data);
                    }
                    reject(error.response?error.response._data:errorMessage);
                });
        });
    }

    // 封装常用方法
    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);
    }
}

// 实例化 HttpRequest 并导出
const httpRequest = new HttpRequest();
export default httpRequest;

2.文件api.ts

import httpRequest from "~/utils/request";

export const index_portal = () => {
    return httpRequest.post('/v1/index/get_index_portal')
}

export const register_email_code = (params: any) => {
    return httpRequest.post('/v1/auth/verify-codes/email', params)
}

新建plugins目录

1.文件同样request.ts 这里是$fetch水合请求的

export default defineNuxtPlugin(() => {
    const config = useRuntimeConfig();

    const $request = $fetch.create({
        baseURL: config.public.apiUrl,
        /** 请求拦截器 */
        onRequest({ options }) => {
            const userAuth = useCookie("token");
            if (userAuth.value) {
                options.headers = options.headers ?? {};
                (options.headers as Record<stringstring>).Authorization = `Bearer ${userAuth.value}`;
            }
        },
        /** 请求错误拦截器,比如服务器无法连接会触发 */
        onRequestError({ request, error }) {},
        /** 响应拦截器 */
        onResponse({ response }) {
            // 成功的状态才进行解包
            if (response.status >= 200 && response.status < 300) {
                response._data = response._data ?? null;
            }
        },
        /** 响应错误拦截器 */
        onResponseError(_context) {}
    });

    return {
        provide: {
            request: $request
        }
    };
});

请求案例

const getQuestion = async()=>{
  const res:any = await get_questions({
    page: page.value,
    limit: pageSize.value,
  })
  if (res.code == 20000) {
    list.value = res.data.list
    total.value = res.data.total
  }
}

代理转发配置

根目录创建.env

# 请求接口地址
VITE_REQUEST_BASE_URL = '/v1'
VITE_SERVER_NAME = 'https://xxx.xxx.com/api'

新建目录server/api

新建文件[…].ts

// @ts-ignore
import {joinURL} from "ufo";
// @ts-ignore
import {proxyRequest} from "h3";

export default defineEventHandler(async (event:any) =>{
    const proxyUrl = useRuntimeConfig().normal_url
    const path = event.path.replace(/^/api//,'')
    const target = joinURL(proxyUrl,path)
    return proxyRequest(event,target)
})

在nuxt.config.ts加入以下转发代码

runtimeConfig: { // 运行时常量
    normal_url:process.env.VITE_SERVER_NAME,
    public: { // 客户端-服务的都能访问
      // baseUrl: process.env.VITE_SERVER_NAME
    },
  },

Nuxt3通用Pinia

安装以下模块

pnpm add @pinia-plugin-persistedstate/nuxt
pnpm add @pinia/nuxt
pnpm add pinia-plugin-persist
pnpm add pinia

在nuxt.config.ts引入

modules: [
    '@pinia/nuxt',
    '@pinia-plugin-persistedstate/nuxt',
  ],

新建store目录

随便新建个文件counter.ts

import { defineStore } from 'pinia'

interface CounterState {
    setting:any
}

// @ts-ignore
export const useCounter = defineStore('counter', {
    state: (): CounterState => ({
        setting: {}
    }),

    actions: {
        increment() {
            this.setting
        }
    },
    getters: {
        doubleCount() {
            return this.setting
        }
    },
    persist: process.client && {
        storage: persistedState.cookiesWithOptions({
            sameSite'strict',
        }),
        paths: ['setting']
    }
})