对于如何使用前端脚手架,这里就不在概述,不懂的可以直接查看官网。 官网地址:pro.ant.design/zh-CN 框架的目录结构如图所示: 现在我们开始来移除某些我们不需要的模块。 注意:在移除每一个模块的时候,都要重新运行一下,看项目是否能成功运行,好发现问题。
移除模块
移除husky
一个用来提交前检查代码的规范,保证代码的一致性,一般用于团体协作,个人可以不用使用。 移除相关的命令:
移除mock
mock是官方提供的模拟数据,我们自己有后端接口请求数据,不需要使用到,第一次使用的话,可以直接使用mock来进行查看页面。 使用mock命令:
shell
npm start
使用开发命令:
shell
npm run dev
移除icons和manifest.json
图标和适配移动端所需要的配置,我们用不到直接删除。
移除cname
官方提供的域名映射
移除国际化
我们自己的项目一般只对大陆开放,国际化根本用不到,所以需要删除。 我们使用删除国际化的命令:
shell
npm run i18n-remove
但是在删完国际化会出现以下的错误,这是因为目前的版本中还不支持eslint2022。 以下是删除国际化的正确方法:
- 前端本地执行以下命令:
shell
yarn add eslint-config-prettier --dev
yarn add eslint-plugin-unicorn --dev
- 找到
node_modules/@umijs/lint/dist/config/eslint/index.js
文件,注释掉以下代码
shell
// es2022: true
- 最后执行
npm run i18n-remove
,就能成功删除了 - 删除完成后我们就可以把locales文件夹了。
这里移除完后,如果登录之后,发现页面没有菜单了,我们需要在routes.ts
文件添加name
属性
移除单元测试
移除types
我们自己会使用open API插件去生成后端对应接口和类型。这里是官方提供的接口,我们不需要。
移除swagger
移除oneapi.json
删除完后报错的话,是因为这个文件在config.js文件引用到了,所以我们来这边,可以先临时使用下它提供的在线地址,后面我们会换成自己接口文档地址。
javascript
/**
* @name openAPI 插件的配置
* @description 基于 openapi 的规范生成serve 和mock,能减少很多样板代码
* @doc https://pro.ant.design/zh-cn/docs/openapi/
*/
openAPI: [
{
requestLibPath: "import { request } from '@umijs/max'",
// 或者使用在线的版本
schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json"
// schemaPath: join(__dirname, 'oneapi.json'),
mock: false,
},
],
初始化代码
生成接口地址
刚刚在上面我们有提到openAPI的字眼,现在我们就需要来使用这个插件,来帮助我们生成后端接口文档的请求地址,这样就不用在一个一个的写请求了。后端我这里的使用的是springdoc
,大家可根据实际文档来生成。
typescript
openAPI: [
{
requestLibPath: "import { request } from '@umijs/max'",
schemaPath: 'http://localhost:8090/api/doc-api.html',
projectName: 'backend-center',
},
],
然后我们来执行命令:所有的命令都可以到package.json
查看
shell
npm run openapi
它生成的文件会在servies
文件夹下。
全局请求配置
目录中有个requestErrorConfig文件,它在app.tsx
引入的文件是这样的
typescript
/**
* @name request 配置,可以配置错误处理
* 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
* @doc https://umijs.org/docs/max/request#配置
*/
export const request = {
... errConfig
};
我们可以改下名字,并且配置都直接在requestErrorConfig
这个文件就好了。 目录和导出的模块名直接改成requestConfig
。 在app.tsx引入:
tsx
export const request = requestConfig;
接下来我们创建文件夹constan,并在里面创建index.ts区分开发环境和生成环境地址:
typescript
/**
* 本地后端地址
*/
export const BACKEND_HOST_LOCAL: string = "http://localhost:8090/api";
/**
* 线上后端地址
*/
export const BACKEND_HOST_PROD: string = "https://...";
�在requestConfig
文件中,官方给的错误处理过于复杂,我们直接简化下,一般后端都会传递token给前端,token一般都会保存在对应的localstorage
,这里我对它做了一个封装,然后前端需要在请求头中添加token才能访问对应的接口。
typescript
import type {RequestOptions} from '@@/plugin-request/request';
import {history, RequestConfig} from '@umijs/max';
import {message} from 'antd';
import {localCache} from "@/utils/cache";
import {TOKEN} from "@/constant";
// 与后端约定的响应数据格式
interface ResponseStructure {
success: boolean;
data: any;
errorCode?: number;
errorMessage?: string;
}
// const isDev = process.env.NODE_ENV === 'development';
/**
* @name 错误处理
* pro 自带的错误处理, 可以在这里做自己的改动
* @doc https://umijs.org/docs/max/request#配置
*/
export const requestConfig: RequestConfig = {
// baseURL: isDev ? BACKEND_HOST_LOCAL : BACKEND_HOST_PROD,
baseURL: "/api",
// 请求拦截器
requestInterceptors: [
(config: RequestOptions) => {
// 拦截请求配置,进行个性化处理。
const token = localCache.getCache(TOKEN);
if (config.headers && token) {
config.headers["token"] = 'Bearer ' + token;
}
//进入到主页面,并且没有token就重新登录
if (!token && !location.pathname.includes('/user/login')) {
// 用户未登录,跳转到登录页
message.warning("您还未登录,请先登录!")
window.location.href = `/user/login?redirect=${window.location.href}`;
}
return config
},
],
// 响应拦截器
responseInterceptors: [
(response) => {
//token
const token: string = localCache.getCache(TOKEN);
// 拦截响应数据,进行个性化处理
const {data} = response as unknown as ResponseStructure;
if (!data) {
throw new Error('服务异常');
}
if (!token && !location.pathname.includes('/user/login') && !location.pathname.includes('/user/register')) {
message.error("您还未登录,请先登录!");
history.push("/user/login")
}
return response;
},
],
};
修改全局文件
全局文件中有个getInitialState函数,主要是来保存初始状态的,里面有主题样式
、当前用户
、加载状态
等内容,这里我直接修改成自己后端获取用户信息的接口。
tsx
export async function getInitialState(): Promise<{
settings?: Partial<LayoutSettings>;
currentUser?: API.User;
loading?: boolean;
fetchUserInfo?: () => Promise<API.User | undefined>;
}> {
const fetchUserInfo = async () => {
try {
const res = await getCurrentUser();
return res.data;
} catch (error) {
message.warning("您还未登录,请先登录!")
history.push(loginPath);
}
return undefined;
};
// 如果不是登录页面,执行
const {location} = history;
if (location.pathname !== loginPath && location.pathname !== registerPath) {
const currentUser = await fetchUserInfo();
return {
fetchUserInfo,
currentUser,
settings: defaultSettings as Partial<LayoutSettings>,
};
}
return {
fetchUserInfo,
settings: defaultSettings as Partial<LayoutSettings>,
};
}
�其中getCurrentUser
是封装的api,内容如下
typescript
/** 获取当前用户 GET /user/currentUser */
export async function getCurrentUser(options?: { [key: string]: any }) {
return request<API.currentUserResult>('/user/currentUser', {
method: 'GET',
...(options || {}),
});
}
然后还有个layout的布局配置,有一些我们不需要的直接删掉。 其中里面有头像对应的字段photo
,是通过调用接口后端返回的,水印中的内容也是,都需要修改成自己后端返回的字段。
tsx
// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({initialState}) => {
return {
avatarProps: {
src: initialState?.currentUser?.photo,
title: <AvatarName/>,
render: (_, avatarChildren) => {
return <AvatarDropdown>{avatarChildren}</AvatarDropdown>;
},
},
//水印
waterMarkProps: {
content: initialState?.currentUser?.nickname,
},
footerRender: () => <Footer/>,
onPageChange: () => {
const {location} = history;
// 如果没有登录,重定向到 login
if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath);
}
},
menuHeaderRender: undefined,
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
...initialState?.settings,
};
};
�