封面图片

编程

使用React、Umi Max、Antd Pro搭建前端项目指南

Antd和Antd pro是我认为国内最好用的React UI后台管理系统开发框架。Umi和Umi Max相当于一个脚手架工具,它集成了很多组件,可以很快速的搭建一个React Hello World项目。


环境准备

首先得有 node,并确保 node 版本是 14 或以上。(推荐用 nvm 来管理 node 版本,windows 下推荐用 nvm-windows

mac 或 linux 下安装 nvm。

1$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash 2$ nvm -v 30.39.1

安装 node。

1$ nvm install 16 2$ nvm use 16 3$ node -v 4v16.10.0

然后需要包管理工具。node 默认包含 npm,但也可以选择其他方案,

安装 pnpm。

1curl -fsSL https://get.pnpm.io/install.sh | sh 2-$ pnpm 3-v7.3.0

快速开始

使用 create-umi选择 Ant Design Pro模板,就能使用 @umijs/max来创建项目了。

1$ npx create-umi@latest 2? Pick Umi App Template › - Use arrow-keys. Return to submit. 3 Simple App 4❯ Ant Design Pro 5 Vue Simple App 6 Umi Plugin 7 8Pick Npm Client 9npm 10 ○ cnpm 11 ○ tnpm 12yarn 13pnpm (recommended) 14 15Pick Npm Registry 16npm 17 ● taobao (recommended for China) 18

启动项目

1pnpm run dev

启动之后访问localhost:8000就能查看初始界面了。里面已经集成了几个demo页面。

https://ppsummer.com/blog/preview/16891902WX20230412-105708@2x.png

路由

页面的路由在项目目录下的.umirc.ts中进行配置。默认访问目录中的index.tsx|tx文件。

以下是一个配置参考:

1import { defineConfig } from '@umijs/max'; 2 3export default defineConfig({ 4 antd: {}, 5 access: {}, 6 model: {}, 7 initialState: {}, 8 request: {}, 9 layout: {}, 10 routes: [ 11 { 12 path: '/', 13 redirect: '/home', 14 }, 15 { 16 name: '登录', 17 path: '/login', 18 component: './Login', 19 menuRender: false, 20 headerRender: false, 21 hideInMenu: true 22 }, 23 { 24 name: '用户管理', 25 path: '/user', 26 component: './UserManager', 27 icon: 'UserSwitchOutlined', 28 access: 'canSeeAdmin' 29 } 30 ], 31 npmClient: 'pnpm', 32 proxy: { 33 '/api/v1': { 34 target: 'http://localhost:8081', 35 changeOrigin: true, 36 }, 37 }, 38});

页面布局与菜单

在src/ap.tsx中进行运行时配置。

1import { RunTimeLayoutConfig } from '@umijs/max'; 2 3//页面初始化方法,可以做获取用户信息的操作,之后可以全局给系统使用 4export async function getInitialState() { 5 //首次页面渲染获取用户认证信息,接口自己实现 6 const { data } = await queryAuth() 7 return { ...data } 8} 9 10//首次渲染认证用户是否合法 11export function render(oldRender) { 12 queryAuth().then(res => { 13 if (!res.data.auth) { 14 localStorage.setItem("redirect", history.location.pathname) 15 history.push('/login') 16 oldRender() 17 } else { 18 if (history.location.pathname === '/login') { 19 history.push('/home') 20 } 21 oldRender() 22 } 23 }).catch(err => { 24 return Promise.reject(err) 25 }) 26} 27 28const items: MenuProps['items'] = [ 29 { 30 key: '1', 31 label: '退出登录', 32 icon: <LogoutOutlined />, 33 //登出自己实现 34 onClick: () => logout() 35 }, 36] 37 38export const layout: RunTimeLayoutConfig = (initialState) => { 39 const contentStyle = { 40 backgroundColor: token.colorBgElevated, 41 borderRadius: token.borderRadiusLG, 42 boxShadow: token.boxShadowSecondary, 43 width: 200 44 }; 45 return { 46 // 常用属性 47 title: '系统标题', 48 logo: LOGO, 49 layout: 'mix', 50 // navTheme: 'realDark', 51 contentWidth: 'Fluid', 52 colorPrimary: '#389e0d', 53 style: { 54 minHeight: '98vh' 55 }, 56 breadcrumbRender: (routers = []) => [ 57 { 58 path: '/', 59 breadcrumbName: '首页', 60 }, 61 ...routers, 62 ], 63 onPageChange: (route) => { 64 const { pathname } = route 65 if (pathname !== '/login') { 66 queryUnreadMessages().then(res => { 67 localStorage.setItem("messages", res.data) 68 }) 69 } 70 }, 71 pageTitleRender: false, 72 footerRender: () => <DefaultFooter copyright="Power by 4th Dept" style={{flex: '0 0 auto'}}/>, 73 // 自定义右上角用户信息展现 74 rightRender: (initialState) => [ 75 <Space key={'1'} size={32}> 76 <Badge count={localStorage.getItem("messages")} offset={[8, -4]} > 77 <BellOutlined key={'ring'} style={{fontSize: 18}} onClick={() => history.push('/messages')}/> 78 </Badge> 79 <Dropdown menu={{ items }} 80 dropdownRender={(item) => ( 81 <div style={contentStyle}> 82 <Space direction="vertical" size={2} style={{padding: '6px 12px'}}> 83 <Text strong style={{ fontSize: 16}}>{initialState?.name}</Text> 84 <Text type={"secondary"}>{initialState?.username}</Text> 85 </Space> 86 <Divider style={{ margin: 0 }} /> 87 {React.cloneElement(item as React.ReactElement)} 88 </div> 89 )} 90 placement="bottomLeft" 91 > 92 <span key={'user'} style={{cursor: "pointer"}}><Avatar src={USER} size={28} style={{marginRight: 4, marginBottom: 4}}/>{initialState?.name}</span> 93 </Dropdown> 94 </Space> 95 ], 96 }; 97};

以上是我自己定义的一个布局。有些细节性的东西需要自己完善。配置中的getInitialState()方法是页面初始化时对web数据的一个初始化,这个方法里可以去请求一些后台用户数据。返回的字段里的avatar和用户名等信息用于显示右上角的用户信息,默认自动添加登出下拉框,也可以自己实现。

rightRender这个属性就是利用getInitialState()返回的数据渲染右上角用户信息。

initialState还可以通过umimax的useModel在全局进行获取和设置。

1import { useModel } from '@umijs/max'; 2const { initialState, setInitialState } = useModel('@@initialState'); 3setInitialState({...data, avatar: image})

网络请求

demo中的网络请求是通过OneApi自动生成的,生成的文件放在src/services下,可以配合后端接口swagger自动生成请求接口。

使用也很方便:

1import services from "@/services/user"; 2 3const { 4 modifyUser, 5 queryAuth, 6 updatePass 7} = services.UserController; 8 9/** 10 * 修改用户示例 11 * @param fields 12 */ 13const handleUpdate = async (fields: Auth_API.UserType, id) => { 14 const hide = message.loading('正在修改'); 15 try { 16 const { data } = await modifyUser({ id, ...fields }); 17 hide(); 18 message.success('修改成功'); 19 return true; 20 } catch (error) { 21 hide(); 22 return false; 23 } 24};

打包

1pnpm build

最后会生成一个dist文件,上传到服务器,配合nginx完成部署。

结合这个Spring Boot后台程序教程可以完成一个前后端内容管理项目的开发。

总结

Umi结合Antd可以非常快速,方便的开发一个后台管理应用。Antd封装了很多组件,官网示例做的也非常好。相对来说,Umi的官网文档就显得比较混乱。Antd pro对组件做了更进一步的封装,效果更好,集成也更加便捷。Antd系列可以说是目前国内最好的React UI框架。它不像Tailwindcss只有样式,它还集成了非常多的逻辑功能,跟Vue的Element UI差不多。当然,Antd也推出了支持Vue的版本。总的来说,Ract+Umi+Antd可以更加高效的开发出效果不错的管理系统。

2023年04月12日
在初学者眼中,世界充满了可能;专家眼中,世界大都已经既定。--铃木俊隆