这篇文章主要就是跟着Ant Design Pro的文档,自行过一遍,写下自己的理解,方便以后查看。
部分会直接使用官网中的话来,主要是看得懂晓得在哪更改就可以了
前面的入门及基本使用中的启动项目,文件夹结构就无需再记录,从标题和加载页开始
注意:更改了config中的文件之后,需要重新启动项目
基本使用
标题和加载页
在Antdpro中,它默认提供了标题的Logo和加载时的loading 页面的配置,虽然官网上说默认不需要更改,但在实际开发中这还是会有所改动的
标题和Logo
标题和Logo的改动可以通过在config/defaultSettings.ts
来进行自行更改,当然在Pro的预览界面中通过右边的设置图标来进行更改,再拷贝设置到上述文件中即可
import { Settings as LayoutSettings } from '@ant-design/pro-layout';
const Settings: LayoutSettings & {
pwa?: boolean;
logo?: string;
} = {
//在此处更改设置
//整体风格设置
navTheme: 'dark',
// 主题颜色
primaryColor: '#F5222D',
// 导航模式
layout: 'side',
// 内容区域宽度
contentWidth: 'Fluid',
// 固定头部
fixedHeader: false,
// 固定侧边菜单
fixSiderbar: true,
// logo
logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
// 头部高度
headerHeight: 48,
// 自动分割菜单
splitMenus: false,
// 标题
title: '文档',
};
export default Settings;
例如:
favicon
favicon是浏览器标签页上右侧的图标,可以通过在src\pages\document.ejs
中进行更改
<link rel="icon" href="<%= context.config.publicPath +'favicon.ico'%>" type="image/x-icon" />
//此处我的更改:
<link rel="icon" href="https://himg.bdimg.com/sys/portraitn/item/2f3fcfebb1bbccf0bbafc4d8aef5" type="image/x-icon" />
加载页
由于场景的不同,Pro 中预设了不少的加载页。使用起来可能会有点混淆。
js 加载前
在刚开始进入页面时,js还未加载成功,而html已经加载成功时的loading页面。这个页面的更改在src\pages\document.ejs
中,
<div>
<!-- 更改整体Pro部分的图片 -->
<img
src="https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"
alt="logo"
width="256"
/>
....
<div style="display: flex; align-items: center; justify-content: center">
<!-- 更改最下面一部分的左侧图片 -->
<img
src="https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/newwenku-d8c9b7b0fb.png"
width="32"
style="margin-right: 8px"
/>
<!-- 更改下面文案 -->
测试文档内容
</div>
</div>
更改前的样式:
更改后的样式:
js 加载后
官网的话:
当在项目中打开了代码分割的话,在每次路由切换的时候都会进入一个加载页面,此时就可以在config/config.ts
中进行更改
dynamicImport: {
loading: '@ant-design/pro-layout/es/PageLoading',
}
这里的配置是一个路径的 string,也可以使用别名的配置。
业务中的加载
官网的话:
在实际的项目中,我们有时候需要等待用户信息或者鉴权系统的请求完成后才能展示页面。所以我们让 getInitialState
支持了异步请求,同时在请求时会停止页面的渲染。这种情况下也是需要一个加载页的。我们可以在 src\app.tsx
中配置:
/** 获取用户信息比较慢的时候会展示一个 loading */
export const initialStateConfig = {
loading: <PageLoading />,
};
页面开发
新增页面
图表
图表在进行这些配置时会报错,项目跑不起来,但是不配置这些也可以使用图表。
布局
布局样式
此处的布局样式与上面的标题和Logo配置一致
菜单显示
可以在config/route.ts文件中对菜单进行相对应的配置
//config/route.ts
export const routes: IBestAFSRoute[] = [
{
path: '/welcome',
component: 'IndexPage',
//菜单上显示的名称,没有则不显示。
name: '欢迎', // 兼容此写法
//菜单上显示的 Icon
icon: 'testicon',
// 新页面打开
target: '_blank',
// 不展示顶栏
headerRender: false,
...
// 权限配置,需要与 plugin-access 插件配合使用
access: 'canRead',
// 隐藏子菜单
hideChildrenInMenu: true,
...
// 子项往上提,仍旧展示,
flatMenu: true,
...
},
];
xxxRender:
-
headerRender=false
不显示顶栏 -
footerRender=false
不显示页脚 -
menuRender=false
不显示菜单 -
menuHeaderRender=false
不显示菜单的 title 和 logo
hideInXXX:
- Type:
boolean
hideInXXX 可以让管理 menu 的渲染。
-
hideChildrenInMenu=true
隐藏子菜单 -
hideInMenu=true
隐藏自己和子菜单 -
hideInBreadcrumb=true
在面包屑中隐藏
菜单国际化
菜单的国际化可以通过在config/config.ts
文件中进行配置:
layout: {
//true则为开启国际化
locale: true,
siderWidth: 208,
...defaultSettings,
},
当开启国际化了之后,路由中配置的菜单名字即name
属性,会被当做菜单名国际化的key
值,此时插件就会到locales
文件夹中寻找menu.[key]
相对应的文案,来当做菜单名,默认值为key
。
如果菜单是多级路由假设是二级路由菜单,那么插件就会去locales
文件中查找 menu.[key].[key]
对应的文案,该功能需要配置 @umijs/plugin-locale
使用。
例如:
//config/route.ts
{
name: 'chars',
icon: 'table',
path: '/smile',
component: './Chars',
access: 'userRoute',
},
//locales/zh-CN/menu.ts
'menu.chars': '图表',
导航右上角
在src/app.ts文件中配置头像和用户名,而国际化语言会通过locale文件夹中的文件来展示可切换的语言种类
// src/app.ts
export function getInitialState() {
return {
name: 'testName',
avatar: 'https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/newxueshuicon-a5314d5c83.png',
};
}
在src/component/RightContent/AvatarDropdown.tsx文件中使用
......
<Avatar size="small" className={styles.avatar} src={initialState.avatar} alt="avatar" />
<span className={`${styles.name} anticon`}>{initialState.name}</span>
......
自定义footer
在src/app.ts文件的layout中footerRender进行配置
// src/app.tsx
import React from 'react';
export const layout = {
footerRender: () => {
// xxx
return <xxx />;
},
};
路由配置
数据管理
简单数据流
在Pro中可以使用Model来进行全局的数据共享
一、新建Model文件
在 src/models
目录下新建文件,文件名会成为model
的namespace
.允许使用 ts, js, tsx(不推荐), jsx(不推荐) 四种后缀。
//model/demo.ts
//此时这个 model 的 namespace 就是文件名 demo
export default () => 'Hello World';
二、使用Model
在代码中使用model
,需要从umi中导出useModel
。useModel
是一个 React Custom Hook,传入namespace
即可获取对应model
的返回值。
import React from 'react'
import { useModel } from 'umi'
const Chars = () => {
const message = useModel('demo')
console.log(message);
return (
<>
<div>{message}</div>
</>
)
}
export default Chars
在实际场景中,model还可以包含其他hooks,例如:
新建:
// counter.ts
import { useState, useCallback } from 'react';
export default () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => setCounter((c) => c + 1), []);
const decrement = useCallback(() => setCounter((c) => c - 1), []);
//返回counter值和increment,decrement方法
return { counter, increment, decrement };
};
使用:
import React from 'react'
import { useModel } from 'umi'
const Chars = () => {
const message = useModel('demo')
const { increment, decrement, counter } = useModel('counter')
return (
<>
<div>{counter}</div>
<button onClick={increment}>increment by 1</button>
<button onClick={decrement}>decrement by 1</button>
<div>{message}</div>
</>
)
}
export default Chars
三、性能优化
useModel 可以接受一个可选的第二个参数,可以用于性能优化。当组件只需要使用model中部分的参数,对其他参数毫不关心时,可以传递一个函数用来过滤。该函数的返回值将会替代model中的返回值,成为useModel中的最终返回值
例如:
import React from 'react'
import { useModel } from 'umi'
const Chars = () => {
const message = useModel('demo')
//ret名字可更改
const { increment, decrement } = useModel('counter', (ret) => ({
//只需要这两个方法,不需要counter值
increment: ret.increment,
decrement: ret.decrement,
}))
return (
<>
<button onClick={increment}>increment by 1</button>
<button onClick={decrement}>decrement by 1</button>
<div>{message}</div>
</>
)
}
export default Chars
权限管理
在项目中经常会有不同用户的权限会各有不同,Pro提供了基于umi插件的权限管理方案-@umijs/plugin-access,可以通过定义及使用来完成React组件中操作或者页面显示的权限控制。搭配@alipay/umi-plugin-layout插件使用还能对路由进一步的权限控制。
一、创建
首先新建src/access.ts文件,在该文件中暴露一个函数,来定义用户可以拥有的权限
export default function access(initialState: { currentUser?: API.CurrentUser | undefined }) {
const { currentUser } = initialState || {};
return {
//根据登录的用户名来进行判断
canAdmin: currentUser?.access === 'admin',
canUser: currentUser?.access === 'user',
};
}
二、使用
- 页面内的权限控制
导入umi中的useAccess、Access,使用useAccess来获取权限定义,使用Access组件来用于页面的显示和隐藏
import { useAccess, Access } from 'umi'
const Chars = () => {
const access = useAccess()
console.log(access);
return (
<>
<Access accessible={access.canUser} fallback={<>user用户才可以访问</>}>
<div>Hello User</div>
</Access>
<Access accessible={access.canAdmin} fallback={<>Admin用户才可以访问</>}>
<div>Hello Admin</div>
</Access>
</>
)
}
export default Chars
Admin登录:
user登录:
- 路由和菜单的权限控制
在src/access.ts文件以上的配置的基础上增加以下代码来控制路由和菜单的权限
//src/access.ts
export default function access(initialState: { currentUser?: API.CurrentUser | undefined }) {
const { currentUser } = initialState || {};
return {
//......
adminRoute: () => currentUser?.access === 'admin',
userRoute: () => currentUser?.access === 'user',
};
}
在config/route.ts文件中进行配置
export default [
//......
{
name: 'list.table-list',
icon: 'table',
path: '/list',
component: './TableList',
//admin用户登录才能显示该路由以及菜单显示
access: 'adminRoute',
},
{
name: 'chars',
icon: 'table',
path: '/smile',
component: './Chars',
//user用户登录才能显示该路由以及菜单显示
access: 'userRoute',
},
];
admin登录:
user登录:
全局初始化数据
官网:
几乎大部分中台项目都有一个需求,就是在整个应用加载前请求用户信息或者一些全局依赖的基础数据。这些信息通常会用于 Layout 上的基础信息(通常是用户信息),权限初始化,以及很多页面都可能会用到的基础数据。
- 创建
在src/app.ts文件中添加getInitialState
配置,该配置是一个async的函数,例如:
export async function getInitialState() {
return {
userName: 'testName',
};
}
- 使用
在组件中使用useModel来获取和使用,此时的namespace就是@@initialState
import React from 'react';
import { useModel } from 'umi';
export default () => {
const { initialState, loading, refresh, setInitialState } = useModel('@@initialState');
return <div>{initialState.userName}</div>;
};