Material-UI组件库使用心得与性能优化实战经验分享
项目初期的技术选型
这次的项目是一个内部管理系统,主要功能是管理一些业务数据。客户对界面的要求是“简洁、专业”,并且希望快速上线。考虑到这些需求,我一开始就在想用什么UI框架比较合适。之前做过几个类似的项目,试过Ant Design和Element Plus,虽然都挺好用,但总觉得它们的默认风格偏重,调整起来有点麻烦。
后来想到Material-UI,之前只是简单用过几次,但它的组件库确实很全,设计风格也符合这次的需求。最重要的是,Material-UI的定制化能力很强,改主题特别方便。再加上它基于React,正好我也熟悉React,就直接拍板定了这个技术栈。
最大的坑:性能问题
项目做到一半的时候,突然发现一个很严重的问题:页面加载速度变慢了,尤其是那些有很多表格和表单的页面,滚动时明显卡顿。刚开始我以为是数据量太大导致的,但仔细检查了一下,发现并不是数据本身的问题,而是Material-UI的组件在渲染时占用了太多资源。
具体来说,问题出在两个地方:
- Table组件的性能瓶颈:Material-UI的Table组件在处理大量数据时,没有内置的虚拟滚动功能。当时表格里有上千条数据,每一行都有复杂的样式和操作按钮,结果就是整个页面变得非常卡。
- ThemeProvider的嵌套问题:为了实现多主题切换,我在项目的不同层级嵌套了多个ThemeProvider。后来才发现,这种做法会让React的context频繁更新,进一步拖慢了性能。
折腾了半天,最后找到了两个解决方案:
首先是针对Table组件,我引入了react-window这个库,用来实现虚拟滚动。代码大概长这样:
import React from 'react';
import { FixedSizeList as List } from 'react-window';
import { Table, TableBody, TableCell, TableRow } from '@mui/material';
const Row = ({ index, style, data }) => (
<TableRow style={style}>
<TableCell>{data[index].id}</TableCell>
<TableCell>{data[index].name}</TableCell>
<TableCell>{data[index].email}</TableCell>
</TableRow>
);
const VirtualizedTable = ({ rows }) => {
return (
<Table>
<TableBody>
<List
height={400}
itemCount={rows.length}
itemSize={50}
width={800}
>
{({ index, style }) => <Row index={index} style={style} data={rows} />}
</List>
</TableBody>
</Table>
);
};
export default VirtualizedTable;
这段代码的核心是通过react-window的FixedSizeList组件来限制每次渲染的行数,从而减少DOM节点的数量。实际效果还不错,滚动流畅了很多。
至于ThemeProvider的问题,我最终决定把所有主题配置集中到最顶层,避免多层嵌套。虽然这样灵活性差了一点,但性能提升很明显。
又踩坑了,样式覆盖太麻烦
另一个让我头疼的地方是样式的自定义。Material-UI提供了很多方式来自定义样式,比如styled()、makeStyles、sx prop等等。但问题是,这些方法有时候会互相冲突。
举个例子,项目中有好几个Button组件需要不同的颜色和大小。我一开始用的是sx prop,因为它写起来最方便:
<Button sx={{ backgroundColor: 'primary.main', fontSize: '16px' }}>Submit</Button>
但后来发现这种方式不够灵活,特别是当需要复用样式时,代码显得很冗余。于是我又尝试了styled():
import { styled } from '@mui/material';
const CustomButton = styled(Button)({
backgroundColor: 'primary.main',
fontSize: '16px',
});
export default CustomButton;
这种方法确实好一点,但有些复杂的样式还是搞不定。最后我还是回归到了makeStyles,虽然官方说这个API可能会被废弃,但对我来说它是最顺手的:
import { makeStyles } from '@mui/styles';
const useStyles = makeStyles({
root: {
backgroundColor: 'primary.main',
fontSize: '16px',
'&:hover': {
backgroundColor: 'secondary.main',
},
},
});
const MyButton = () => {
const classes = useStyles();
return <Button className={classes.root}>Submit</Button>;
};
export default MyButton;
说实话,这个问题到最后也没完全解决,因为项目的样式需求一直在变,我只能不停地调整。不过好在最终的效果还算过得去。
核心代码就这几行
除了上面提到的性能优化和样式问题,还有一些小细节值得一提。比如,项目中有一个需求是要根据用户的权限动态显示菜单项。这里用到了Material-UI的Menu组件和条件渲染:
import React from 'react';
import { Menu, MenuItem } from '@mui/material';
const DynamicMenu = ({ permissions }) => {
return (
<Menu>
{permissions.includes('view') && <MenuItem>View</MenuItem>}
{permissions.includes('edit') && <MenuItem>Edit</MenuItem>}
{permissions.includes('delete') && <MenuItem>Delete</MenuItem>}
</Menu>
);
};
export default DynamicMenu;
这段代码很简单,但确实帮我节省了不少时间。
回顾与反思
总体来看,Material-UI在这次项目中的表现还算不错,但也有一些遗憾:
- 做得好的地方:组件库很全,基本不需要额外找第三方库;主题定制能力强,满足了客户对界面风格的要求;文档详细,遇到问题基本都能找到答案。
- 需要改进的地方:性能优化是个大坑,尤其是Table组件;样式自定义的方式太多了,有时候反而让人不知道该选哪个。
如果以后再用Material-UI,我会提前规划好性能优化的方案,尽量避免后期的大改动。另外,样式这块我可能还会再深入研究一下,争取找到一个更统一的解决方案。
以上是我个人对这个Material-UI项目的完整讲解,有更优的实现方式欢迎评论区交流。

暂无评论