Material-UI组件库使用心得与性能优化实战经验分享

Mc.浩然 框架 阅读 2,960
赞 24 收藏
二维码
手机扫码查看
反馈

项目初期的技术选型

这次的项目是一个内部管理系统,主要功能是管理一些业务数据。客户对界面的要求是“简洁、专业”,并且希望快速上线。考虑到这些需求,我一开始就在想用什么UI框架比较合适。之前做过几个类似的项目,试过Ant Design和Element Plus,虽然都挺好用,但总觉得它们的默认风格偏重,调整起来有点麻烦。

Material-UI组件库使用心得与性能优化实战经验分享

后来想到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-windowFixedSizeList组件来限制每次渲染的行数,从而减少DOM节点的数量。实际效果还不错,滚动流畅了很多。

至于ThemeProvider的问题,我最终决定把所有主题配置集中到最顶层,避免多层嵌套。虽然这样灵活性差了一点,但性能提升很明显。

又踩坑了,样式覆盖太麻烦

另一个让我头疼的地方是样式的自定义。Material-UI提供了很多方式来自定义样式,比如styled()makeStylessx 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项目的完整讲解,有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论