
Material UI v4 到 v5 迁移样式与主题破坏性变更实战指南【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Googles Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-uiMaterial UI v5 默认样式库从 JSS 切换为 Emotion并重构了主题对象的结构这使 v4 → v5 的迁移中“样式与主题”成为破坏性变更最集中的部分。本文基于官方迁移文档v5-style-changes.md逐项讲解 styleOverrides 选择器、主题结构、mui/material/styles导出、System 属性等全部变更并结合当前仓库的源码实现adaptV4Theme.js、createPalette.js、colorManipulator.js说明每项变更的底层行为。读完本文你可以对照检查清单逐项完成样式与主题层面的迁移。迁移总览五步流程与 codemods 的作用官方将 v5 迁移拆为五个部分本篇覆盖第二部分样式与主题完整流程为Getting started主迁移指南见 迁移文档目录Breaking changes part one: style and theme本文主题Breaking changes part two: components组件级变更Migrating from JSSTroubleshooting。v5 引入了大量破坏性变更其中很多可以借助官方 codemods 自动解决——codemods 的实现就在本仓库的 mui-codemod 包 中。判断标准很简单在目录中用 ✅ 标记的变更项由 codemods 自动处理如果你已按主迁移指南跑过 codemods这些条目无需再手动操作其余条目必须手动处理。将主题的 styleOverrides 迁移到 Emotion重构本地规则引用$语法主题中定义的样式覆盖在 v5 中可能“看起来还能工作”但嵌套元素的样式机制已经改变JSS 时代的$本地规则引用语法在 Emotion 下不再有效必须替换为合法的全局类选择器。替换状态类名state classconst theme createTheme({ components: { MuiOutlinedInput: { styleOverrides: { root: { - $focused: { .Mui-focused: { borderWidth: 1, } } } } } });将嵌套类选择器替换为全局类名const theme createTheme({ components: { MuiOutlinedInput: { styleOverrides: { root: { - $notchedOutline: { .MuiOutlinedInput-notchedOutline: { borderWidth: 1, } } } } } });更稳妥的做法是利用官方导出的[component]Classes常量避免硬编码类名字符串import { outlinedInputClasses } from mui/material/OutlinedInput; const theme createTheme({ components: { MuiOutlinedInput: { styleOverrides: { root: { - $notchedOutline: { [ .${outlinedInputClasses.notchedOutline}]: { borderWidth: 1, } } } } } });所有组件都导出了包含其全部嵌套类的[component]Classes常量可以放心依赖它而不是手写类名。完整的全局状态类名列表见 Customization 文档的 “State classes” 章节。重构空格/逗号分隔值的替代数组语法JSS 支持用嵌套数组表达空格与逗号分隔的值如多背景、多段 paddingEmotion 不支持这种语法需要改写成字符串。背景多值示例Beforeconst theme createTheme({ overrides: { MuiBox: { root: { background: [ [url(image1.png), no-repeat, top], [url(image2.png), no-repeat, center], !important, ], }, }, }, });Afterconst theme createTheme({ components: { MuiBox: { styleOverrides: { root: { background: url(image1.png) no-repeat top, url(image2.png) no-repeat center !important, }, }, }, }, });注意为数值补上单位// Before padding: [[5, 8, 6]], // After padding: 5px 8px 6px,这一点与下文theme.spacing返回值带 px 后缀的变更一致Emotion 不会自动为数字补单位凡是需要像素值的地方务必显式写明。ref 相关的破坏性变更移除对非 ref-forwarding 类组件的支持componentprop 或作为直接children传入的非 ref 转发类组件其支持已被移除。如果你之前使用了unstable_createStrictModeTheme或在React.StrictMode下从未见过与findDOMNode相关的警告则无需处理否则请阅读 Composition 指南中 “Caveat with refs” 章节了解迁移方式。此变更几乎影响所有使用componentprop 的组件以及要求 children 必须是元素的场景例如MenuListCustomMenuItem //MenuList。收紧 ref 的类型约束部分组件传入ref时会出现类型错误需要使用更具体的元素类型。例如Card期望HTMLDivElementListItem期望HTMLLIElementimport * as React from react; import Card from mui/material/Card; import ListItem from mui/material/ListItem; export default function SpecificRefType() { - const cardRef React.useRefHTMLElement(null); const cardRef React.useRefHTMLDivElement(null); - const listItemRef React.useRefHTMLElement(null); const listItemRef React.useRefHTMLLIElement(null); return ( div Card ref{cardRef}/Card ListItem ref{listItemRef}/ListItem /div ); }各组件期望的具体元素类型mui/material组件ref 类型AccordionHTMLDivElementAlertHTMLDivElementAvatarHTMLDivElementButtonGroupHTMLDivElementCardHTMLDivElementDialogHTMLDivElementImageListHTMLUListElementListHTMLUListElementTabHTMLDivElementTabsHTMLDivElementToggleButtonHTMLButtonElementmui/lab组件ref 类型TimelineHTMLUListElement样式库调整 CSS 注入顺序v5 默认样式库为 Emotion。如果你仍在用 JSS例如makeStyles为 Material UI 组件做覆盖就必须处理两套style的注入顺序JSS 的style元素必须在 Emotion 的style元素之后注入到head否则你的覆盖会被 Material UI 自身样式压过。✅ 使用 StyledEngineProvider 调整注入顺序将带injectFirst选项的StyledEngineProvider放在组件树顶层import * as React from react; import { StyledEngineProvider } from mui/material/styles; export default function GlobalCssPriority() { return ( {/* Inject Emotion before JSS */} StyledEngineProvider injectFirst {/* Your component tree. Now you can override Material UIs styles. */} /StyledEngineProvider ); }✅ 为自定义 Emotion cache 添加 prepend如果你已有自定义 cache 并用 Emotion 给应用写样式它会覆盖 Material UI 提供的 cache。修正注入顺序的方式是给createCache加prepend选项import * as React from react; import { CacheProvider } from emotion/react; import createCache from emotion/cache; const cache createCache({ key: css, prepend: true, }); export default function PlainCssPriority() { return ( CacheProvider value{cache} {/* Your component tree. Now you can override Material UIs styles. */} /CacheProvider ); }:::warning 如果使用的是 styled-components且StyleSheetManager带有自定义target请确保该 target 是 HTMLhead中的第一个元素。可参考mui/styled-engine-sc包中StyledEngineProvider的实现位于packages/mui-styled-engine-sc/src目录。 :::主题结构Theme structure变更v5 中主题对象的形状发生了重构所有组件相关配置统一收拢到components键下。为了平滑过渡官方提供了adaptV4Theme辅助函数可渐进式地将旧主题升级为新结构。✅ 使用 adaptV4Theme 辅助函数-import { createMuiTheme } from mui/material/styles; import { createTheme, adaptV4Theme } from mui/material/styles; -const theme createMuiTheme({ const theme createTheme(adaptV4Theme({ // v4 theme -}); }));源码印证adaptV4Theme.js 展示了适配器实际做的事——将 v4 的defaultProps/props映射到components[组件名].defaultPropsL28-L39将styleOverrides/overrides映射到components[组件名].styleOverridesL41-L52并重新生成theme.spacingL54-L55。需要注意的是源码开头L4-L12在开发环境下会打印adaptV4Theme() is deprecated的警告即它只是过渡工具最终仍需手动迁移到 v5 原生结构。:::warning 该适配器只处理createTheme()的入参。如果你在创建主题后修改了主题形状结构必须手动迁移。 :::以下是适配器支持的各项具体变更移除 gutters 抽象“gutters” 抽象被证明使用频率不够高而移除。注意从源码结构看适配器仍会在开发过渡期为旧主题补回mixins.gutters的等价实现adaptV4Theme.js L57-L75paddingLeft/paddingRight: spacing(2)在sm断点以上升级为spacing(3)但新代码应直接写-theme.mixins.gutters(), paddingLeft: theme.spacing(2), paddingRight: theme.spacing(2), [theme.breakpoints.up(sm)]: { paddingLeft: theme.spacing(3), paddingRight: theme.spacing(3), },✅ theme.spacing 返回值带 px 后缀theme.spacing现在默认返回带 px 单位的字符串。这一变更改善了与 styled-components 和 Emotion 的集成Emotion 不会给数字自动补单位// Before theme.spacing(2) 16 // After theme.spacing(2) 16px✅ theme.palette.type 重命名为 modetheme.palette.type键重命名为theme.palette.mode以贴合描述该功能的 “dark mode” 惯用术语import { createTheme } from mui/material/styles; -const theme createTheme({ palette: { type: dark } }), const theme createTheme({ palette: { mode: dark } }),从源码看适配器同时写入mode与type两个键adaptV4Theme.js L86-L87保证过渡期间新旧代码都能读到正确的模式值。默认 theme.palette.info 颜色变更默认info色被调整为在亮色与暗色模式下都通过 WCAG AA 无障碍对比度标准info { - main: cyan[500], main: lightBlue[700], // lightBlue[400] in dark mode - light: cyan[300], light: lightBlue[500], // lightBlue[300] in dark mode - dark: cyan[700], dark: lightBlue[900], // lightBlue[700] in dark mode }默认 theme.palette.success 颜色变更success { - main: green[500], main: green[800], // green[400] in dark mode - light: green[300], light: green[500], // green[300] in dark mode - dark: green[700], dark: green[900], // green[700] in dark mode }默认 theme.palette.warning 颜色变更warning { - main: orange[500], main: #ED6C02, // orange[400] in dark mode - light: orange[300], light: orange[500], // orange[300] in dark mode - dark: orange[700], dark: orange[900], // orange[700] in dark mode }源码印证createPalette.js 中的getDefaultInfoL161-L174、getDefaultSuccessL176-L189、getDefaultWarningL191-L204与上述取值完全一致其中 warning 的main取#ed6c02源码注释说明这是“最接近 orange[800] 且能通过 3:1 对比度”的值L200。按需恢复 theme.palette.text.hint 键theme.palette.text.hint键在 Material UI 组件中未被使用已被移除。如果业务代码依赖它可以手动加回import { createTheme } from mui/material/styles; -const theme createTheme(), const theme createTheme({ palette: { text: { hint: rgba(0, 0, 0, 0.38) } }, });注意适配器恢复该键时会按模式区分默认值暗色模式为rgba(255, 255, 255, 0.5)亮色模式为rgba(0, 0, 0, 0.38)adaptV4Theme.js L81-L85。重构组件定义主题中的组件定义被重组到components键下便于查找。1. props → components[组件].defaultPropsimport { createTheme } from mui/material/styles; const theme createTheme({ - props: { - MuiButton: { - disableRipple: true, - }, - }, components: { MuiButton: { defaultProps: { disableRipple: true, }, }, }, });2. overrides → components[组件].styleOverridesimport { createTheme } from mui/material/styles; const theme createTheme({ - overrides: { - MuiButton: { - root: { padding: 0 }, - }, - }, components: { MuiButton: { styleOverrides: { root: { padding: 0 }, }, }, }, });mui/stylesJSS 迁移包相关变更v5 不再内置 JSS原mui/material/styles中的 JSS 工具被拆分到独立的mui/styles包已标记废弃。更新 ThemeProvider 导入如果同时使用mui/styles工具与mui/material应改用mui/material/styles导出的ThemeProvider这样上下文中的theme同时对makeStyles、withStyles等mui/styles工具与 Material UI 组件可见-import { ThemeProvider } from mui/styles; import { ThemeProvider } from mui/material/styles;由于mui/styles工具不再提供defaultTheme务必在应用根部添加一个ThemeProvider。✅ 为 DefaultTheme 添加模块增强TypeScriptmui/styles包不再属于mui/material/styles。如果两者并用需要为DefaultTheme添加模块增强module augmentation// in the file where you are creating the theme (invoking the function createTheme()) import { Theme } from mui/material/styles; declare module mui/styles { interface DefaultTheme extends Theme {} }mui/material/colors颜色导入路径变更✅ 变更颜色导入方式超过一层的嵌套导入是私有的不能再从mui/material/colors/red导入red-import red from mui/material/colors/red; import { red } from mui/material/colors;mui/material/styles 导出项变更这是变更密度最高的部分几乎所有 JSS 时代从mui/material/styles导出的工具都移动到了mui/styles。✅ fade 重命名为 alphafade()重命名为alpha()以更好描述其功能。旧名在输入颜色本身已带 alpha 值时容易引起误解——该工具会覆盖颜色的 alpha 通道。-import { fade } from mui/material/styles; import { alpha } from mui/material/styles; const classes makeStyles(theme ({ - backgroundColor: fade(theme.palette.primary.main, theme.palette.action.selectedOpacity), backgroundColor: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), }));源码印证colorManipulator.js 中alpha()L245-L259先对颜色值做decomposeColor解析再把 alpha 通道直接赋值为传入参数value被钳制在 0-1 区间文档注释明确写着 “Any existing alpha values are overwritten”L240与上述行为一致。✅ 更新 createStyles 导入createStyles从mui/material/styles移到mui/styles导出目的是从 Material UI npm 包中移除对mui/styles的依赖-import { createStyles } from mui/material/styles; import { createStyles } from mui/styles;✅ 更新 createGenerateClassName 导入createGenerateClassName不再从mui/material/styles导出。若需继续使用该函数可从已废弃的mui/styles包导入-import { createGenerateClassName } from mui/material/styles; import { createGenerateClassName } from mui/styles;不使用mui/styles而生成自定义类名可参考文档中的 ClassName Generatorexperimental-api章节。✅ createMuiTheme 重命名createMuiTheme重命名为createTheme()使其与ThemeProvider搭配使用时更直观-import { createMuiTheme } from mui/material/styles; import { createTheme } from mui/material/styles; -const theme createMuiTheme({ const theme createTheme({✅ 更新 MuiThemeProvider 导入MuiThemeProvider组件不再从mui/material/styles导出改用ThemeProvider-import { MuiThemeProvider } from mui/material/styles; import { ThemeProvider } from mui/material/styles;✅ 更新 jssPreset 导入jssPreset对象不再从mui/material/styles导出可从已废弃的mui/styles包继续导入-import { jssPreset } from mui/material/styles; import { jssPreset } from mui/styles;✅ 更新 makeStyles 导入Material UI v5 不再使用 JSS基于 JSS 的makeStyles不再由mui/material/styles导出。在迁移期间可临时从mui/styles/makeStyles导入这个已废弃的工具之后再逐步重构组件。由于defaultTheme不再可用务必在应用根部添加ThemeProvider与mui/material并用时推荐使用mui/material/styles的ThemeProvider。-import { makeStyles } from mui/material/styles; import { makeStyles } from mui/styles; import { createTheme, ThemeProvider } from mui/material/styles; const theme createTheme(); const useStyles makeStyles((theme) ({ background: theme.palette.primary.main, })); function Component() { const classes useStyles(); return div className{classes.root} / } // In the root of your app function App(props) { - return Component /; return ThemeProvider theme{theme}Component {...props} //ThemeProvider; }✅ 更新 ServerStyleSheets 导入ServerStyleSheets不再从mui/material/styles导出可从已废弃的mui/styles包导入-import { ServerStyleSheets } from mui/material/styles; import { ServerStyleSheets } from mui/styles;styledv5 中原 JSS 版styled被一个不向后兼容的 Emotion 等价实现取代。迁移期间可临时从mui/styles导入 JSS 版之后再重构。同样注意defaultTheme不可用需手动提供ThemeProvider-import { styled } from mui/material/styles; import { styled } from mui/styles; import { createTheme, ThemeProvider } from mui/material/styles; const theme createTheme(); const MyComponent styled(div)(({ theme }) ({ background: theme.palette.primary.main })); function App(props) { - return MyComponent /; return ThemeProvider theme{theme}MyComponent {...props} //ThemeProvider; }✅ 更新 StylesProvider 导入StylesProvider不再从mui/material/styles导出可从已废弃的mui/styles包导入-import { StylesProvider } from mui/material/styles; import { StylesProvider } from mui/styles;✅ 更新 useThemeVariants 导入useThemeVariants钩子不再从mui/material/styles导出可从已废弃的mui/styles包导入-import { useThemeVariants } from mui/material/styles; import { useThemeVariants } from mui/styles;✅ 更新 withStyles 导入与makeStyles同理JSS 版withStyles不再由mui/material/styles导出迁移期间可临时从mui/styles/withStyles导入并手动在根部提供ThemeProvider-import { withStyles } from mui/material/styles; import { withStyles } from mui/styles; import { createTheme, ThemeProvider } from mui/material/styles; const defaultTheme createTheme(); const MyComponent withStyles((props) { const { classes, className, ...other } props; return div className{clsx(className, classes.root)} {...other} / })(({ theme }) ({ root: { background: theme.palette.primary.main }})); function App() { - return MyComponent /; return ThemeProvider theme{defaultTheme}MyComponent //ThemeProvider; }✅ 用 ref 替换 innerRef将innerRefprop 替换为refpropref 现在会自动转发到内部组件import * as React from react; import { withStyles } from mui/styles; const MyComponent withStyles({ root: { backgroundColor: red, }, })(({ classes }) div className{classes.root} /); function MyOtherComponent(props) { const ref React.useRef(); - return MyComponent innerRef{ref} /; return MyComponent ref{ref} / }更新 withTheme 导入withThemeHOC 已从mui/material/styles移除可改用mui/styles/withTheme。同样需要手动提供ThemeProvider-import { withTheme } from mui/material/styles; import { withTheme } from mui/styles; import { createTheme, ThemeProvider } from mui/material/styles; const theme createTheme(); const MyComponent withTheme(({ theme }) div{theme.direction}/div); function App(props) { - return MyComponent /; return ThemeProvider theme{theme}MyComponent {...props} //ThemeProvider; }✅ 移除 withWidth该 HOC 已被移除。如需同等能力可用useMediaQuery钩子实现替代方案见文档 react-use-media-query 的 “migrating-from-withwidth” 章节。mui/icons-materialGitHub 图标尺寸调整GitHub 图标宽度从 24px 缩小到 22px以与其他图标尺寸保持一致。这是一处无感知的视觉修正无需代码改动。material-ui/pickersmaterial-ui/pickers迁移到 v5 有专门文档pickers-migration 页面不在本文范围内请查阅官方文档。System 变更✅ 重命名 gap 相关 props以下 System 函数与属性因属于被废弃的 CSS 写法而重命名gridGap→gapgridRowGap→rowGapgridColumnGap→columnGap✅ gap 属性需要带间距单位在gap、rowGap、columnGap中使用间距单位。如果你之前传的是数字现在需要显式写 px以绕开基于theme.spacing的新换算逻辑Box - gap{2} gap2px 源码印证gap的样式函数注册在 System 的默认 sx 配置中见 defaultSxConfig.tsgap: { style: gap }grid 布局场景下的 gap 处理还可参考 gridGenerator.ts 与 cssGrid.ts。用 sx 替换 css prop为避免与 styled-components 和 Emotion 的cssprop 冲突cssprop 改为sx-Box css{{ color: primary.main }} / Box sx{{ color: primary.main }} /:::warning v4 中 System 的 grid 函数并未被文档化因此不存在对应迁移项。 :::迁移自查清单完成本文各节后建议按以下顺序自查主题对象是否已改用components键defaultPropsstyleOverridespalette.mode是否替换了typestyleOverrides 中是否还残留$本地规则引用或嵌套数组值是否已运行 codemods✅ 标记项若无残留报错可跳过与 JSS/Emotion 混用场景StyledEngineProvider injectFirst或createCache({ prepend: true })是否就位从mui/material/styles导入的makeStyles、withStyles、styled、withTheme等是否已改为从mui/styles导入并在根部提供ThemeProviderTypeScript 项目DefaultTheme模块增强是否已添加ref 类型是否收紧到具体元素类型System 用法gridGap系列是否改为gap/rowGap/columnGap并显式带上 px 单位cssprop 是否替换为sx完成以上内容后即可进入 v5 迁移的第三部分——组件级破坏性变更v5-component-changes继续完成整个迁移流程。【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Googles Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考