(
+ url: string,
+ params?: AxiosRequestConfig,
+ config?: PureHttpRequestConfig
+ ): Promise {
+ return this.request("post", url, params, config);
+ }
+
+ /** 单独抽离的`get`工具函数 */
+ public get(
+ url: string,
+ params?: AxiosRequestConfig,
+ config?: PureHttpRequestConfig
+ ): Promise {
+ return this.request("get", url, params, config);
+ }
+
/** 请求拦截 */
private httpInterceptorsRequest(): void {
PureHttp.axiosInstance.interceptors.request.use(
@@ -144,51 +193,6 @@ class PureHttp {
}
);
}
-
- /** 通用请求工具函数 */
- public request(
- method: RequestMethods,
- url: string,
- param?: AxiosRequestConfig,
- axiosConfig?: PureHttpRequestConfig
- ): Promise {
- const config = {
- method,
- url,
- ...param,
- ...axiosConfig
- } as PureHttpRequestConfig;
-
- // 单独处理自定义请求/响应回调
- return new Promise((resolve, reject) => {
- PureHttp.axiosInstance
- .request(config)
- .then((response: undefined) => {
- resolve(response);
- })
- .catch(error => {
- reject(error);
- });
- });
- }
-
- /** 单独抽离的`post`工具函数 */
- public post(
- url: string,
- params?: AxiosRequestConfig,
- config?: PureHttpRequestConfig
- ): Promise {
- return this.request("post", url, params, config);
- }
-
- /** 单独抽离的`get`工具函数 */
- public get(
- url: string,
- params?: AxiosRequestConfig,
- config?: PureHttpRequestConfig
- ): Promise {
- return this.request("get", url, params, config);
- }
}
export const http = new PureHttp();
diff --git a/src/utils/http/mockRequest.ts b/src/utils/http/mockRequest.ts
new file mode 100644
index 0000000..fb5f196
--- /dev/null
+++ b/src/utils/http/mockRequest.ts
@@ -0,0 +1,191 @@
+import Axios, {
+ type AxiosInstance,
+ type AxiosRequestConfig,
+ type CustomParamsSerializer
+} from "axios";
+import type {
+ PureHttpError,
+ PureHttpRequestConfig,
+ PureHttpResponse,
+ RequestMethods
+} from "./types.d";
+import { stringify } from "qs";
+import NProgress from "../progress";
+import { formatToken, getToken } from "@/utils/auth";
+import { useUserStoreHook } from "@/store/modules/user";
+
+// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
+const defaultConfig: AxiosRequestConfig = {
+ timeout: import.meta.env.VITE_BASE_API_TIMEOUT,
+ baseURL: import.meta.env.VITE_MOCK_BASE_API || "/mock",
+ headers: {
+ Accept: "application/json, text/plain, */*",
+ "Content-Type": "application/json",
+ "X-Requested-With": "XMLHttpRequest"
+ },
+ // 数组格式参数序列化(https://github.com/axios/axios/issues/5142)
+ paramsSerializer: {
+ serialize: stringify as unknown as CustomParamsSerializer
+ }
+};
+
+class PureHttp {
+ /** `token`过期后,暂存待执行的请求 */
+ private static requests = [];
+ /** 防止重复刷新`token` */
+ private static isRefreshing = false;
+ /** 初始化配置对象 */
+ private static initConfig: PureHttpRequestConfig = {};
+ /** 保存当前`Axios`实例对象 */
+ private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
+
+ constructor() {
+ this.httpInterceptorsRequest();
+ this.httpInterceptorsResponse();
+ }
+
+ /** 重连原始请求 */
+ private static retryOriginalRequest(config: PureHttpRequestConfig) {
+ return new Promise(resolve => {
+ PureHttp.requests.push((token: string) => {
+ config.headers["Authorization"] = formatToken(token);
+ resolve(config);
+ });
+ });
+ }
+
+ /** 通用请求工具函数 */
+ public request(
+ method: RequestMethods,
+ url: string,
+ param?: AxiosRequestConfig,
+ axiosConfig?: PureHttpRequestConfig
+ ): Promise {
+ const config = {
+ method,
+ url,
+ ...param,
+ ...axiosConfig
+ } as PureHttpRequestConfig;
+
+ // 单独处理自定义请求/响应回调
+ return new Promise((resolve, reject) => {
+ PureHttp.axiosInstance
+ .request(config)
+ .then((response: undefined) => {
+ resolve(response);
+ })
+ .catch(error => {
+ reject(error);
+ });
+ });
+ }
+
+ /** 单独抽离的`post`工具函数 */
+ public post(
+ url: string,
+ params?: AxiosRequestConfig,
+ config?: PureHttpRequestConfig
+ ): Promise {
+ return this.request("post", url, params, config);
+ }
+
+ /** 单独抽离的`get`工具函数 */
+ public get(
+ url: string,
+ params?: AxiosRequestConfig,
+ config?: PureHttpRequestConfig
+ ): Promise {
+ return this.request("get", url, params, config);
+ }
+
+ /** 请求拦截 */
+ private httpInterceptorsRequest(): void {
+ PureHttp.axiosInstance.interceptors.request.use(
+ async (config: PureHttpRequestConfig): Promise => {
+ // 开启进度条动画
+ NProgress.start();
+ // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
+ if (typeof config.beforeRequestCallback === "function") {
+ config.beforeRequestCallback(config);
+ return config;
+ }
+ if (PureHttp.initConfig.beforeRequestCallback) {
+ PureHttp.initConfig.beforeRequestCallback(config);
+ return config;
+ }
+ /** 请求白名单,放置一些不需要`token`的接口(通过设置请求白名单,防止`token`过期后再请求造成的死循环问题) */
+ const whiteList = ["/refresh-token", "/login"];
+ return whiteList.some(url => config.url.endsWith(url))
+ ? config
+ : new Promise(resolve => {
+ const data = getToken();
+ if (data) {
+ const now = new Date().getTime();
+ const expired = parseInt(data.expires) - now <= 0;
+ if (expired) {
+ if (!PureHttp.isRefreshing) {
+ PureHttp.isRefreshing = true;
+ // token过期刷新
+ useUserStoreHook()
+ .handRefreshToken({ refreshToken: data.refreshToken })
+ .then(res => {
+ const token = res.data.accessToken;
+ config.headers["Authorization"] = formatToken(token);
+ PureHttp.requests.forEach(cb => cb(token));
+ PureHttp.requests = [];
+ })
+ .finally(() => {
+ PureHttp.isRefreshing = false;
+ });
+ }
+ resolve(PureHttp.retryOriginalRequest(config));
+ } else {
+ config.headers["Authorization"] = formatToken(
+ data.accessToken
+ );
+ resolve(config);
+ }
+ } else {
+ resolve(config);
+ }
+ });
+ },
+ error => {
+ return Promise.reject(error);
+ }
+ );
+ }
+
+ /** 响应拦截 */
+ private httpInterceptorsResponse(): void {
+ const instance = PureHttp.axiosInstance;
+ instance.interceptors.response.use(
+ (response: PureHttpResponse) => {
+ const $config = response.config;
+ // 关闭进度条动画
+ NProgress.done();
+ // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
+ if (typeof $config.beforeResponseCallback === "function") {
+ $config.beforeResponseCallback(response);
+ return response.data;
+ }
+ if (PureHttp.initConfig.beforeResponseCallback) {
+ PureHttp.initConfig.beforeResponseCallback(response);
+ return response.data;
+ }
+ return response.data;
+ },
+ (error: PureHttpError) => {
+ const $error = error;
+ $error.isCancelRequest = Axios.isCancel($error);
+ // 关闭进度条动画
+ NProgress.done();
+ // 所有的响应异常 区分来源为取消请求/非取消请求
+ return Promise.reject($error);
+ }
+ );
+ }
+}
+
+export const http = new PureHttp();
diff --git a/types/global.d.ts b/types/global.d.ts
index 0126664..45fe1b1 100644
--- a/types/global.d.ts
+++ b/types/global.d.ts
@@ -65,6 +65,7 @@ declare global {
*/
interface ViteEnv {
VITE_PORT: number;
+ VITE_APP_URL: string;
VITE_PUBLIC_PATH: string;
VITE_ROUTER_HISTORY: string;
VITE_CDN: boolean;
diff --git a/vite.config.ts b/vite.config.ts
index 144c3a2..85751a7 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,13 +1,9 @@
import { getPluginsList } from "./build/plugins";
import { exclude, include } from "./build/optimize";
import { type ConfigEnv, loadEnv, type UserConfigExport } from "vite";
-import {
- __APP_INFO__,
- alias,
- pathResolve,
- root,
- wrapperEnv
-} from "./build/utils";
+import { __APP_INFO__, alias, root, wrapperEnv } from "./build/utils";
+import { serverOptions } from "./build/server";
+import { buildEnvironment } from "./build/buildEnv";
export default ({ mode }: ConfigEnv): UserConfigExport => {
const { VITE_CDN, VITE_PORT, VITE_COMPRESSION, VITE_PUBLIC_PATH } =
@@ -15,45 +11,19 @@ export default ({ mode }: ConfigEnv): UserConfigExport => {
return {
base: VITE_PUBLIC_PATH,
root,
- resolve: {
- alias
- },
+ resolve: { alias },
// 服务端渲染
- server: {
- // 端口号
- port: VITE_PORT,
- host: "0.0.0.0",
- // 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy
- proxy: {},
- // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布
- warmup: {
- clientFiles: ["./index.html", "./src/{views,components}/*"]
- }
- },
+ server: serverOptions(mode),
plugins: getPluginsList(VITE_CDN, VITE_COMPRESSION, VITE_PORT),
// https://cn.vitejs.dev/config/dep-optimization-options.html#dep-optimization-options
- optimizeDeps: {
- include,
- exclude
- },
- build: {
- // https://cn.vitejs.dev/guide/build.html#browser-compatibility
- target: "es2015",
- sourcemap: false,
- // 消除打包大小超过500kb警告
- chunkSizeWarningLimit: 4000,
- rollupOptions: {
- input: {
- index: pathResolve("./index.html", import.meta.url)
- },
- // 静态资源分类打包
- output: {
- chunkFileNames: "static/js/[name]-[hash].js",
- entryFileNames: "static/js/[name]-[hash].js",
- assetFileNames: "static/[ext]/[name]-[hash].[ext]"
- }
- }
+ optimizeDeps: { include, exclude },
+ esbuild: {
+ pure: ["console.log", "debugger"],
+ jsxFactory: "h",
+ jsxFragment: "Fragment",
+ jsxInject: "import { h } from 'vue';"
},
+ build: buildEnvironment(),
define: {
__INTLIFY_PROD_DEVTOOLS__: false,
__APP_INFO__: JSON.stringify(__APP_INFO__)