umi官网:快速上手
Umi Max:Umi Max 简介
proComponent官网:https://procomponents.ant.design
一、环境准备(建议)
1. 首先你的电脑上必须有 node 并且版本在 14+
// 检查当前node版本
$ node -v
v16.10.0
2. 安装 pnpm。
// 查看当前镜像源
$ npm config get registry
// 切换淘宝镜像源
$ npm config set registry https://registry.npm.taobao.org/
// 全局安装pnpm
$ npm install -g pnpm
// 查看pnpm版本
$ pnpm -v
7.3.0
二、创建项目
1. 找个地方建个空目录。
$ mkdir myapp && cd myapp
2. 通过官方工具创建项目
$ pnpm dlx create-umi@latest
注意:这里第一个选项Ant Design Pro,我们使用的是 @umijs/max
来创建项目
3. 启动项目
$ pnpm dev
umi官网会自动帮我们生成框架,我们的项目文件和项目是这样的
4. 目录结构
参考官网:目录结构
.
├── mock //mock接口数据文件
│ └── app.ts|tsx
├── node_modules //存放依赖文件
├── src
│ ├── .umi //dev 时的临时文件目录。
│ ├── .umi-production //build 时的临时文件目录。
│ ├── assets
│ │ └── .gitkeep //无意义,起到占位符的作用
│ ├── components //公共组件
│ │ └── Model
│ ├── constants //定义常量文件
│ │ └── index.ts
│ ├── models //全局状态管理文件
│ │ └──global.ts
│ ├── pages //页面组件
│ │ └── Home
│ ├── services // 接口文件
│ │ └── demo
│ ├── utils // 工具文件
│ │ └── format.ts
│ ├── app.(ts|tsx) //运行时配置 文件。
│ └──access.ts //定义项目中的权限。
├── .eslintrc.js //规则
├── .gitignore //配置git忽略文件或目录
├── .lintstagedrc
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .stylelintrc.js
├── .umirc.ts //配置文件,包含 Umi 所有非运行时配置
├── package.json //Webpack配置和项目包管理文件
├── pnpm-lock.yaml
├── README.md
├── tsconfig.json
└── typings.d.ts
5. 主题
因为公司要求的主题是偏暗黑风格,所以我们一起来配置一下主题色调吧
点击进入主题编辑器 - Ant Design 定制主题风格,点击保存,点击主题配置并复制。
然后打开.umirc.ts文件,找到antd属性,把我们的代码复制上去,更多属性可以参考 umi/max antd
6. 布局
因为布局太过大众化,所以我们先打开app.ts文件,稍微修改一下我们的布局。然后再打开.umirc.ts文件,把我们的路由稍微调整一下。
splitMenus属性为我们提供了横向和纵向分离的布局,来看下效果拔(分离之后是不是更加简洁了呢)。
7. 自定义右上角用户
如果你的用户只需要定义一个退出登录的功能,直接在app.ts文件中添加logout函数即可;
如果需要定义更多功能,则需要使用rightRender自定义渲染,除此之外我们还能自定义一些结果页面203,404,错误页面等。
三、配置
1. 路由
// .umirc.ts 文件
export default defineConfig({
routes: [
{ path: '/', component: 'index' },
{ path: '/user', component: 'user' },
],
})
2. 代理
// .umirc.ts 文件
export default defineConfig({
proxy: {
'/api': {
'target': 'http://jsonplaceholder.typicode.com/',
'changeOrigin': true,
// 'pathRewrite': { '^/api' : '' },
},
},
})
3. 配置请求
// app.tsx 文件
import { RequestConfig } from '@umijs/max';
export const request: RequestConfig = {
// timeout: 3000,
// other axios options you want
errorConfig: {
errorHandler() {},
errorThrower() {},
},
// 请求拦截器
requestInterceptors: [
// 直接写一个 function,作为拦截器
(url, options) => {
// do something
return { url, options };
},
// 一个二元组,第一个元素是 request 拦截器,第二个元素是错误处理
[
(url, options) => {
return { url, options };
},
(error) => {
return Promise.reject(error);
},
],
// 数组,省略错误处理
[
(url, options) => {
return { url, options };
},
],
],
// 响应拦截器
responseInterceptors: [
// 直接写一个 function,作为拦截器
(response) => {
// 不再需要异步处理读取返回体内容,可直接在data中读出,部分字段可在 config 中找到
// const { data = {} as any, config } = response;
// do something
console.log(response);
return response;
},
// 一个二元组,第一个元素是 request 拦截器,第二个元素是错误处理
[
(response) => {
return response;
},
(error) => {
return Promise.reject(error);
},
],
// 数组,省略错误处理
[
(response) => {
return response;
},
],
],
};
发送请求
import { request } from '@umijs/max';
export const getFindList = (params: {
// query
/** keyword */
key?: string;
/** current */
page?: number;
}) =>
request<API.Result>(
`/union/shop/discovery/${params.key}/${params.page}`,
);
4. 公共状态管理
// src/models/global.ts
import { useState } from 'react';
const useUser = () => {
const [name, setName] = useState<string>("你好! 同学");
return {
name,
setName,
};
};
export default useUser;
// src/pages/Home/index.tsx
import { useModel } from '@umijs/max';
const HomePage: React.FC = () => {
const { name , setName } = useModel('global');//根据文件名获取
return (
<div>
<p>{name}</p>
<button onClick={()=>setName("你好!UMI")}> 按钮</button>
</div>
);
};
export default HomePage;
5. 环境变量
$ pnpm install cross-env -D
package.json修改命令
"scripts": {
"build": "cross-env UMI_ENV=release max build", //生产环境打包
"build:test": "cross-env UMI_ENV=trial max build", //测试环境打包
"dev": "cross-env UMI_ENV=develop max dev", //开发环境本地运行
"dev:test": "cross-env UMI_ENV=trial max dev", //测试环境本地运行
},
根目录下创建文件.umirc.develop.ts
import { defineConfig } from '@umijs/max';
export default defineConfig({
define: {
'process.env': {
BASE_URL: '开发环境地址',
},
},
})
根目录下创建文件.umirc.trial.ts
import { defineConfig } from '@umijs/max';
export default defineConfig({
define: {
'process.env': {
BASE_URL: '测试环境地址',
},
},
})
根目录下创建文件.umirc.release.ts
import { defineConfig } from '@umijs/max';
export default defineConfig({
define: {
'process.env': {
BASE_URL: '生产环境地址',
},
},
})
项目中使用
console.log('UMI_ENV', process.env.BASE_URL);
6. 打包(白屏问题)
在.umirc.ts文件中添加
export default defineConfig({
hash: true,
history: {
type: 'hash', // 可选 browser、hash 和 memory
},
base: '/',
publicPath: process.env.NODE_ENV === 'production' ? './' : '',
});
pnpm build
7. 登录
使用umi/max登录的逻辑,是在登录页面拿到token
// 页面
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { useNavigate } from '@umijs/max';
import { Button, Card, Form, Input, message } from 'antd';
import { Cookies } from 'react-cookie';
import './index.less';
export default () => {
const navigate = useNavigate();
const onFinish = (values: any) => {
const username = values.username === 'admin';
const password = values.password === '123456';
if (username && password) {
const cookies = new Cookies();
cookies.set('user-token', '5tgb3edc7ytfc8ujhn');
message.success('登录成功');
navigate('/home');
} else {
message.error('账号或密码错误');
}
};
return (
<div className="login-pages">
<Card title="登录" className="login-card">
<Form wrapperCol={{ offset: 4, span: 16 }} onFinish={onFinish}>
<Form.Item
name="username"
rules={[{ required: true, message: '请输入 账号' }]}
>
<Input prefix={<UserOutlined />} placeholder="用户名或手机号" />
</Form.Item>
<Form.Item
name="password"
rules={[{ required: true, message: '请输入 密码!' }]}
>
<Input.Password prefix={<LockOutlined />} placeholder="密码" />
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 8 }}>
<Button type="primary" htmlType="submit" className="login-button">
登录
</Button>
</Form.Item>
</Form>
</Card>
</div>
);
};
// 样式
.login-pages{
height: 100vh;
min-height: 276px;
width: 100vw;
min-width: 600px;
background: #eeeeee;
padding-top: calc(50vh - 136px);
}
.login-card {
width: 420px;
margin: auto;
}
.login-button {
width: 100%;
}
getInitialState 统一获取、管理用户信息
// app.tsx文件 用户信息数据流
import { Cookies } from 'react-cookie';
export async function getInitialState(): Promise<{ userInfo: API.UserInfo }> {
// 获取token
const cookies = new Cookies();
console.log(cookies.get('user-token'));
// 根据token获取请求用户数据
const userInfo = {
id: 1,
userName: 'admin',
nickName: '王布尔',
isAdmin: false,
avatar:
'https://img.alicdn.com/imgextra/i2/O1CN01Jd8bIr1gZUwzJDpdp_!!6000000004156-0-tps-720-540.jpg',
};
// 此处return的数据可用useModel获取,也可以用作权限管理
return {
userInfo,
};
}
export const layout = () => {
return {
...,
pure: location.hash === '#/login' ? true : false, //如果为登录页面就隐藏系统布局
};
};
8. 路由权限
// access.ts 权限
export default (initialState: { userInfo: API.UserInfo }) => {
// 此处的initialState是app.tsx文件中getInitialState()函数反回的用户数据
// 在这里按照初始化数据定义项目中的权限,统一管理
// 参考文档 https://umijs.org/docs/max/access
// console.log('--------', initialState);
return {
isAdmin: initialState?.isAdmin ?? false,
};
};
// app.tsx 配置错误跳转的页面
export const layout = () => {
return {
...,
// 自定义 403 页面
unAccessible: <UnAccessible />,
// 自定义 404 页面
noFound: <NoFound />,
// Ant Design Pro 的错误页
errorBoundary: <ErrorBoundary />,
};
};
// route.ts
{
name: '地图',
path: '/file/map',
component: './FileManagement/Map',
access: 'isAdmin', //判断isAdmin是否为true为页面添加权限
},
最后建议。如果.umirc配置较为复杂,建议采用.config文件夹代替配置项目。