采用vite方式 ,根据选择 react-ts
pnpm create vite
vite.config:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias:{
"@":path.resolve(__dirname,'./src')//配置@别名
}
},
})
Tips:为了ts不报错需要配置 tsconfig.json
tsconfig.json:
具体tsconfig配置参数详解
{
"compilerOptions": {
"target": "ESNext",// 指定ECMAScript目标版本
"useDefineForClassFields": true,//此标志用作迁移到即将推出的类字段标准版本的一部分
"lib": ["DOM", "DOM.Iterable", "ESNext"],//用于指定需要包含的模块,只有在这里列出的模块的声明文件才会被加载
"allowJs": false, // 允许 ts 编译器编译 js 文件
"skipLibCheck": true, // 跳过声明文件的类型检查
"esModuleInterop": false,// es 模块互操作,屏蔽 ESModule和CommonJS之间的差异
"allowSyntheticDefaultImports": true, // 允许通过import x from 'y' 即使模块没有显式指定 default 导出
"strict": true,//true => 同时开启 alwaysStrict, noImplicitAny, noImplicitThis 和 strictNullChecks
"forceConsistentCasingInFileNames": true, // 对文件名称强制区分大小写
"module": "ESNext",// 指定生成哪个模块系统代码
"moduleResolution": "Node",// 模块解析(查找)策略
"resolveJsonModule": true,// 防止 ts文件中引入json文件,会报如下红色波浪线
"isolatedModules": true,// 是否将没有 import/export 的文件视为旧(全局而非模块化)脚本文件。
"noEmit": true, // 编译时不生成任何文件(只进行类型检查)
"jsx": "react-jsx", // 指定将 JSX 编译成什么形式
"baseUrl": "./src",//配置paths前先配置baseUrl
"paths": {
"@/*": ["*"], // 模块名到基于 baseUrl的路径映射的列表
},
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
❗❗❗❗如找不到path或找不到__dirname等
Tips:如图报node自带模块的错误,需要安装 @types/node
pnpm add @types/node --save-dev
pnpm add react-router-dom@6 --save-dev
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
);
3.在router文件夹下创建index.tsx
import { Routes, Route } from "react-router-dom";
import {lazy} from "react";
const Home = lazy(() => import("@/pages/home"))
const Login = lazy(() => import("@/pages/login"))
function RootRoute() :JSX.Element{
return (
<>
} />
} />
>
);
}
export default RootRoute
Tips:path为路径,element为引入的路由文件。
4.在app.tsx 引入router文件即可
1.下载
pnpm add antd
2.引入antd样式
Tips:在入口文件 引入的css文件里如app.css(注意在最上方)
@import'antd/dist/antd.css';
3使用
Tips:根据antd文档 在使用的地方子回家引入 如:
import { Button } from 'antd';
1.下载
pnpm add axios -S
2.构建项目目录 如图 配置正式测试环境地址
2.1 新增http文件夹 在其中生产如下文件
2.2 新建.env 区分环境
根据实际情况配置
修改package.json 启动命令
"scripts": {
"dev": "vite serve --mode development",
"build:pro": "tsc && vite build --mode production",
},
3.编写index文件
具体逻辑可以新增
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
import { AxiosCanceler } from "./helper/axiosCancel";
import { checkStatus } from "./helper/checkStatus";
import { message } from 'antd'
enum ResultEnum {
SUCCESS = 200,
ERROR = 500,
OVERDUE = 10001,
TIMEOUT = 6000,
TYPE = "success"
}
interface Result {
code: number;
message: string;
}
// * 请求响应参数(包含data)
interface ResultData extends Result {
data?: T;
}
const axiosCanceler = new AxiosCanceler();
const config = {
// 默认地址请求地址,可在 .env 开头文件中修改
baseURL: import.meta.env.VITE_APP_BASE_API as string,
// 设置超时时间(10s)
timeout: ResultEnum.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true
};
class RequestHttp {
service: AxiosInstance;
constructor(config: AxiosRequestConfig) {
// 实例化axios
this.service = axios.create(config);
/**
* @description 请求拦截器
*/
this.service.interceptors.request.use(
(config: AxiosRequestConfig) => {
axiosCanceler.addPending(config);
// * 需要添加的token 自行设置
const token: string|null = '';
return { ...config, headers: { "token": token } };
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
/**
* @description 响应拦截器
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data, config } = response;
// * 在请求结束后,移除本次请求
axiosCanceler.removePending(config);
// * 登陆失效操作
if (data.code == ResultEnum.OVERDUE) {
message.error(data.message);
return Promise.reject(data);
}
// * 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错)
if (data.code && data.code !== ResultEnum.SUCCESS) {
return Promise.reject(data);
}
// * 成功请求
return data;
},
async (error: AxiosError) => {
const { response } = error;
// 根据响应的错误状态码,做不同的处理
if (response) return checkStatus(response.status);
// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
if (!window.navigator.onLine) return
return Promise.reject(error);
}
);
}
// * 常用请求方法封装
get(url: string, params?: any, _object = {}): Promise> {
return this.service.get(url, { params, ..._object });
}
post(url: string, params?: object, _object = {}): Promise> {
return this.service.post(url, params, _object);
}
put(url: string, params?: object, _object = {}): Promise> {
return this.service.put(url, params, _object);
}
delete(url: string, params?: any, _object = {}): Promise> {
return this.service.delete(url, { params, ..._object });
}
}
export default new RequestHttp(config);
4.checkStatus.ts
import { message } from 'antd'
/**
* @description: 校验网络请求状态码
* @param {Number} status
* @return void
*/
export const checkStatus = (status: number): void => {
switch (status) {
case 400:
message.error("请求失败!请您稍后重试");
break;
case 401:
message.error("登录失效!请您重新登录");
break;
case 403:
message.error("当前账号无权限访问!");
break;
case 404:
message.error("你所访问的资源不存在!");
break;
case 405:
message.error("请求方式错误!请您稍后重试");
break;
case 408:
message.error("请求超时!请您稍后重试");
break;
case 500:
message.error("服务异常!");
break;
case 502:
message.error("网关错误!");
break;
case 503:
message.error("服务不可用!");
break;
case 504:
message.error("网关超时!");
break;
default:
message.error("请求失败!");
}
};
5.axiosCancel.ts
Tips:先下载qs模块
pnpm add qs
如确认下载qs模块后 遇见qs报错 则需要 下载@types/qs
pnpm add @types/qs -D
import axios, { AxiosRequestConfig, Canceler } from "axios";
import qs from "qs";
const isFunction(val: unknown) {
return toString.call(val) === `[object Function]`;
}
// * 声明一个 Map 用于存储每个请求的标识 和 取消函数
let pendingMap = new Map();
// * 序列化参数
export const getPendingUrl = (config: AxiosRequestConfig) =>
[config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join("&");
export class AxiosCanceler {
/**
* @description: 添加请求
* @param {Object} config
* @return void
*/
addPending(config: AxiosRequestConfig) {
// * 在请求开始前,对之前的请求做检查取消操作
this.removePending(config);
const url = getPendingUrl(config);
config.cancelToken =
config.cancelToken ||
new axios.CancelToken(cancel => {
if (!pendingMap.has(url)) {
// 如果 pending 中不存在当前请求,则添加进去
pendingMap.set(url, cancel);
}
});
}
/**
* @description: 移除请求
* @param {Object} config
*/
removePending(config: AxiosRequestConfig) {
const url = getPendingUrl(config);
if (pendingMap.has(url)) {
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
const cancel = pendingMap.get(url);
cancel && cancel();
pendingMap.delete(url);
}
}
/**
* @description: 清空所有pending
*/
removeAllPending() {
pendingMap.forEach(cancel => {
cancel && isFunction(cancel) && cancel();
});
pendingMap.clear();
}
/**
* @description: 重置
*/
reset(): void {
pendingMap = new Map();
}
}