MUI v4 makeStyles中使用props方法引发大量组件渲染变慢问题咨询
问题确认
这个是Material UI v4的已知设计缺陷,并非代码逻辑导致的Bug。v4版本makeStyles的底层逻辑只要检测到样式定义中存在任意函数类型的样式值,每次调用useStyles传参时,都会全量遍历所有样式规则、执行所有规则函数,无论对应的样式类是否被组件实际使用,这就是你观测到未使用的样式函数也会反复执行、渲染变慢的核心原因。该问题受限于v4的样式架构,没有在后续v4小版本中彻底修复,直到v5版本更换了样式解决方案才完全解决。
可行优化方案(均支持props控制样式、读取theme配置)
方案1:拆分静态与动态样式规则
把不需要props控制的静态样式,和需要props控制的动态样式拆分到不同的makeStyles实例中,静态样式的makeStyles完全不包含函数类型定义,避免不必要的全量遍历:
// 静态样式,无任何函数定义,性能无损耗 const useStaticStyles = makeStyles(({ typography }) => ({ fontFamilySecondary: { fontFamily: typography.fontFamilySecondary, }, bold: { fontWeight: typography.fontWeightBold, }, boldScondary: { fontWeight: typography.fontWeightBoldSecondary, }, italic: { fontStyle: "italic", }, })) // 仅包含需要props控制的动态样式,遍历规则数量极少,性能损耗可忽略 const useDynamicStyles = makeStyles(() => ({ fontFamily: { fontFamily: ({ fontFamily }: any) => fontFamily, }, })) // 组件内调用 const staticClasses = useStaticStyles() const styleProps = useMemo(() => ({ fontFamily }), [fontFamily]) const dynamicClasses = useDynamicStyles(styleProps) // 合并类名使用即可 const classes = {...staticClasses, ...dynamicClasses}
方案2:用类名切换替代props传参
如果动态样式的取值范围有限,提前定义好所有可选的静态样式类,配合clsx工具根据props切换类名,完全避免在makeStyles中写函数,性能最优:
import clsx from 'clsx' const useStyles = makeStyles(({ typography }) => ({ fontSans: { fontFamily: typography.fontFamilySans }, fontSerif: { fontFamily: typography.fontFamilySerif }, fontMono: { fontFamily: typography.fontFamilyMono }, // 其余静态样式 })) // 组件内调用 const classes = useStyles() const textClass = clsx({ [classes.fontSans]: fontFamily === 'sans', [classes.fontSerif]: fontFamily === 'serif', [classes.fontMono]: fontFamily === 'mono', })
方案3:使用MUI v4内置的styled API
styledAPI的样式计算是和组件绑定的,不会全量遍历无关规则,性能远高于带函数的makeStyles:
import { styled } from '@material-ui/core/styles' const CustomText = styled('div')(({ theme, fontFamily }) => ({ fontFamily: fontFamily || theme.typography.fontFamily, fontWeight: theme.typography.fontWeightBold, // 其余样式 })) // 组件内使用 <CustomText fontFamily={fontFamily}>内容</CustomText>
补充优化点
- 传给
useStyles的props必须用useMemo缓存,只有依赖的属性变化时才重新生成对象,避免不必要的样式重计算,这也是你之前单独用useMemo没生效的常见原因 - 尽量减少
makeStyles中函数类型规则的数量,能抽成静态类的都抽成静态类 - 如果业务允许升级依赖,推荐升级到MUI v5版本,v5的样式方案从底层解决了这个性能问题
内容的提问来源于stack exchange,提问作者Tony Gustafsson
相关产品推荐
相关产品推荐

