Material UI v7 升级指南:从 v6 迁移的完整路径——包布局、Grid 改名、废弃 API 移除与 codemod 实操
本文基于 Material UI 仓库中官方的 v7 升级文档 upgrade-to-v7.md 展开,系统讲解从 Material UI v6 升级到 v7 的每一步:包布局(package exports)变更、Grid/Grid2 组件改名、InputLabel size 属性标准化、主题行为变化,以及 v5 起被废弃 API 的移除细节。读完后你将掌握:需要升级/保持不变的 npm 包清单、React 18 及以下的 react-is 版本对齐方案、所有破坏性变更的代码修改方式(diff 形式),以及如何用仓库内置的 @mui/codemod 自动完成大部分迁移工作。
说明:本仓库当前已演进到更新的版本(如 packages/mui-material/package.json 中
version字段为 9.x),但 v6 → v7 的迁移要求仍以官方升级文档为准,本文即按该文档如实整理并结合仓库源码验证。
一、为什么要升级到 v7
1.1 改进的 ESM 支持
v7 更新了包的目录布局,通过 package.json 中的 exports 字段同时、无歧义地支持合法的 ESM 与 CommonJS 两种模块形态(exports 语义可参考 Node.js 官方包规范文档)。这一更新修复了 Vite、webpack 等主流打包器的一系列问题,并使得 MUI 各包可以在 Node.js 下通过 ES 模块直接加载。
从当前仓库的 packages/mui-material/package.json 可以直接看到这套 exports 布局的落地形态:
"exports": {
".": "./src/index.js",
"./ButtonBase/TouchRipple": "./src/ButtonBase/TouchRipple.js",
"./Grid": "./src/Grid/index.ts",
...
"./*": "./src/*/index.js"
}
注意最后一条 "./*": "./src/*/index.js" 通配规则:它只允许一层深度的子路径导入。这正是下文「深层导入失效」这一破坏性变更的根源——例如 @mui/material/styles/createTheme(两层)不再可解析,而 @mui/material/Grid(一层)仍然合法。
1.2 其他质量改进
- 全部组件的 slot 模式(slot pattern)用法统一化;
- 支持 CSS layers:客户端应用通过
StyledEngineProvider的enableCssLayer属性开启,Next.js App Router 应用则通过AppRouterCacheProvider开启; - 移除了已废弃的 API,缩小 API 面,使文档更易于导航。
1.3 需要升级到 7.0.0 的包清单
如果你使用了以下任何包,都应把版本同步升到 "7.0.0":
@mui/icons-material@mui/system@mui/lab@mui/material-nextjs@mui/styled-engine@mui/styled-engine-sc@mui/utils
而 MUI X 系列包不遵循与 Material UI 相同的版本策略,升级过程中应保持版本不变:
@mui/x-data-grid、@mui/x-data-grid-pro、@mui/x-data-grid-premium@mui/x-date-pickers、@mui/x-date-pickers-pro@mui/x-charts@mui/x-tree-view、@mui/x-tree-view-pro
二、最低 TypeScript 版本提升至 4.9
v7 将最低支持的 TypeScript 版本从 v4.7 提升到 4.9。官方说明其对齐依据是 DefinitelyTyped(npm 上 @types 命名空间背后的类型库)的支持窗口策略;并承诺不会在 Material UI 的 minor 版本中再次变更最低支持版本。
对于 @types/react* 系列包,需确保其主版本与你项目使用的 react 主版本一致。按需执行(把 <version> 替换为你使用的 react 主版本号):
# npm
npm install @types/react@<version> @types/react-dom@<version>
# pnpm
pnpm add @types/react@<version> @types/react-dom@<version>
# yarn
yarn add @types/react@<version> @types/react-dom@<version>
官方提醒:完成这一步后,务必确认应用仍能无错误运行,并先提交(commit)这些改动,再继续后续迁移步骤——这样每一步迁移的 diff 都可以独立回滚。
三、React 18 及更低版本:对齐 react-is 版本
3.1 操作步骤
如果你使用 React 18 或更低版本,需要把 react-is 强制解析为与你 react 版本一致的版本。以 react@18.3.1 为例:
第 1 步:安装对应版本的 react-is
# npm
npm install react-is@18.3.1
# pnpm
pnpm add react-is@18.3.1
# yarn
yarn add react-is@18.3.1
第 2 步:在 package.json 中配置 overrides / resolutions
// npm
{
"overrides": {
"react-is": "^18.3.1"
}
}
// pnpm
{
"overrides": {
"react-is": "^18.3.1"
}
}
// yarn
{
"resolutions": {
"react-is": "^18.3.1"
}
}
3.2 为什么需要这样做
Material UI v7 依赖 react-is@19(仓库 packages/mui-material/package.json 中可见 react-is 依赖为 ^19.2.8),而 react-is@19 改变了 React 元素的识别方式。如果你停留在 React 18 或更低版本,react-is 版本不一致会在 prop type 检查中触发运行时错误;把 react-is 强制锁定为你的 React 版本即可避免这类问题。
四、破坏性变更逐项拆解
v7 是一个新的主版本,包含影响公共 API 的变更。以下按官方文档逐项给出修改方法。
4.1 包布局更新(Node.js exports 字段)
深层导入(超过一层)全面失效——它们本就被视为私有 API,现在会被打包器和运行时明确限制:
-import createTheme from '@mui/material/styles/createTheme';
+import { createTheme } from '@mui/material/styles';
modern 捆绑包被移除。此前 bundle 体积优化空间已不显著,如果你曾为这些 bundle 配置过 alias,必须现在删除:
{
resolve: {
alias: {
- '@mui/material': '@mui/material/modern',
- '@mui/styled-engine': '@mui/styled-engine/modern',
- '@mui/system': '@mui/system/modern',
- '@mui/base': '@mui/base/modern',
- '@mui/utils': '@mui/utils/modern',
- '@mui/lab': '@mui/lab/modern',
}
}
}
官方同时说明:早期版本指南曾提到存在 mui-modern 条件导出,现已移除;这属于非破坏性变化,打包器会自然回退到 ESM 捆绑包。
Vite 中针对 icons 包的强制 ESM alias 也应删除,因为已不再必要:
// vite.config.js
resolve: {
alias: [
- {
- find: /^@mui\/icons-material\/(.*)/,
- replacement: "@mui/icons-material/esm/$1",
- },
],
},
主题类型增强(module augmentation):如果你曾对嵌套导入路径声明模块增强,需要改为增强 @mui/material/styles;部分接口在新路径下导出名不同,可能需要同名重命名:
-declare module '@mui/material/styles/createTypography' {
+declare module '@mui/material/styles' {
- interface TypographyOptions {
+ interface TypographyVariantsOptions {
// ...
}
- interface Typography {
+ interface TypographyVariants {
// ...
}
}
4.2 Grid 与 Grid2 改名
这是 v7 中影响面最大的组件级变更,有三条路径可选:
已废弃的 Grid 组件被改名为 GridLegacy;Grid2 组件则迁入 Grid 命名空间。
路径 1:使用旧 Grid 且希望升级 —— 运行官方 codemod:
npx @mui/codemod@latest v7.0.0/grid-props <path/to/folder>
该 codemod 的仓库源码位于 grid-props/index.js。从 grid-props.js 的实现可以看出它的能力边界:它同时识别来自 @mui/material/Grid、@mui/system/Grid、@mui/joy/Grid 的默认导入以及 @mui/material / @mui/system / @mui/joy 桶导入中的具名 Grid;默认断点集合为 xs, sm, md, lg, xl,并支持通过 muiBreakpoints 选项传入自定义断点、通过 packageName 选项扩展识别的包名;此外它对 .json 与 .d.ts 文件直接跳过。
路径 2:继续使用旧 Grid(改名跟随) —— 更新所有 Grid 引用:
// imports
-import Grid, { gridClasses, GridProps } from '@mui/material/Grid';
+import Grid, { gridLegacyClasses, GridLegacyProps } from '@mui/material/GridLegacy';
-import { Grid } from '@mui/material';
+import { GridLegacy as Grid } from '@mui/material';
// theme
const theme = createTheme({
components: {
- MuiGrid: {
+ MuiGridLegacy: {
// ...
},
},
});
// CSS classes
-.MuiGrid-root
+.MuiGridLegacy-root
路径 3:使用 Grid2(迁入新命名空间) —— 更新所有 Grid2 引用:
// imports
-import Grid, { grid2Classes as gridClasses, Grid2Props as GridProps } from '@mui/material/Grid2';
+import Grid, { gridClasses, GridProps } from '@mui/material/Grid';
-import { Grid2 as Grid } from '@mui/material';
+import { Grid } from '@mui/material';
// theme
const theme = createTheme({
components: {
- MuiGrid2: {
+ MuiGrid: {
// ...
},
},
});
// CSS classes
-.MuiGrid2-root
+.MuiGrid-root
4.3 InputLabel 的 size 属性标准化
InputLabel 的 size 属性从此遵循 Button、TextField 等组件的通用命名约定:'normal' 被 'medium' 取代。若你显式使用过 size="normal",改为 size="medium":
-<InputLabel size="normal">Label</InputLabel>
+<InputLabel size="medium">Label</InputLabel>
默认行为不变(未显式设置 size 的代码无需改动)。官方提供 codemod 自动完成替换:
npx @mui/codemod@latest v7.0.0/input-label-size-normal-medium <path/to/folder>
其实现 input-label-size-normal-medium.js 基于 jscodeshift:查找所有 InputLabel JSX 元素,将其 size 属性中值为 'normal' 的静态字面量(含字符串表达式形式)直接改写为 j.literal('medium'),动态值则保持不动。
注意:由于 InputLabel 的默认 size 从 normal 变为 medium,类名 MuiInputLabel-sizeMedium 将不再被添加。如果你依赖该类名做自定义样式,请改用其他类。
4.4 SvgIcon 移除 data-testid
@mui/icons-material 中图标的默认 data-testid 属性在生产构建中被移除。该变更确保 data-testid 只在需要处定义,降低命名冲突的可能,并去除生产环境中的冗余属性。
4.5 TablePaginationActions 类型导入路径变更
类型导入路径由 @mui/material/TablePagination/TablePaginationActions 改为 @mui/material/TablePaginationActions:
- import type { TablePaginationActionsProps } from '@mui/material/TablePagination/TablePaginationActions';
+ import type { TablePaginationActionsProps } from '@mui/material/TablePaginationActions';
4.6 主题行为变化:cssVariables + 双色方案下 theme 对象不再随 mode 切换
当启用 CSS 主题变量并同时内置 light/dark 双色方案时,切换深浅模式后 theme 对象本身不再变化(useColorScheme 的 mode 状态会变,但 useTheme() 返回的对象不再变)。官方示例:
import {
ThemeProvider,
createTheme,
useTheme,
useColorScheme,
} from '@mui/material/styles';
const theme = createTheme({
cssVariables: {
colorSchemeSelector: 'class',
},
colorSchemes: {
light: true,
dark: true,
},
});
console.log(theme.palette.mode); // 'light' is the default mode
function ColorModeToggle() {
const { setMode, mode } = useColorScheme();
const theme = useTheme();
React.useEffect(() => {
console.log(mode); // 首次渲染输出 'light',点击按钮后输出 'dark'
}, [mode]);
React.useEffect(() => {
// 仅首次渲染输出 'light',点击按钮后不再触发
console.log(theme.palette.mode);
}, [theme]);
return <button onClick={() => setMode('dark')}>Toggle dark mode</button>;
}
function App() {
return (
<ThemeProvider theme={theme}>
<ColorModeToggle />
</ThemeProvider>
);
}
这一默认行为的目的是避免切换模式时产生不必要的重渲染,从而提升性能。相应的样式写法建议调整为:
推荐:用 theme.vars.* 直接引用 CSS 变量
const Custom = styled('div')(({ theme }) => ({
color: theme.vars.palette.text.primary,
background: theme.vars.palette.primary.main,
}));
需要运行时计算时:优先用 CSS 而非 JavaScript,例如借助 color-mix 函数调整颜色透明度(可参考 MDN 上 color-mix 的文档):
const Custom = styled('div')(({ theme }) => ({
color: `color-mix(in srgb, ${theme.vars.palette.text.primary}, transparent 50%)`,
}));
CSS 方案不可行时:从 theme.colorSchemes 分别取 light/dark 的值并同时应用两种模式样式
const Custom = styled('div')(({ theme }) => ({
color: alpha(theme.colorSchemes.light.palette.text.primary, 0.5),
...theme.applyStyles('dark', {
color: alpha(theme.colorSchemes.dark.palette.text.primary, 0.5),
}),
}));
兜底:opt-out —— 若以上方式均不适合项目,可给 ThemeProvider 传入 forceThemeRerender 属性恢复旧行为:
<ThemeProvider forceThemeRerender />
该属性在仓库源码 ThemeProvider.tsx 中有对应实现与测试(同目录 ThemeProviderWithVars.test.js),可确认其是 v7 起正式的公共 API。
4.7 v5 起废弃的 API 已移除
以下 API 在 v7 中被正式移除。逐项给出替代方案:
createMuiTheme 函数
已移除,改用 createTheme:
-import { createMuiTheme } from '@mui/material/styles';
+import { createTheme } from '@mui/material/styles';
Dialog 的 onBackdropClick 属性
已移除,改用 onClose 回调(接收事件对象与关闭原因 reason):
function Example() {
const [open, setOpen] = React.useState(false);
const handleClose = (event, reason) => {
if (reason === 'backdropClick') {
// 处理背景点击
}
setOpen(false);
};
return (
<Dialog open={open} onClose={handleClose}>
{/* Dialog content */}
</Dialog>
);
}
experimentalStyled 函数
已移除,改用 styled:
-import { experimentalStyled as styled } from '@mui/material/styles';
+import { styled } from '@mui/material/styles';
Hidden 与 PigmentHidden 组件
已移除。implementation="css" 用 sx 属性替代:
-<Hidden implementation="css" xlUp><Paper /></Hidden>
+<Paper sx={{ display: { xl: 'none', xs: 'block' } }} />
-<Hidden implementation="css" mdDown><Paper /></Hidden>
+<Paper sx={{ display: { xs: 'none', md: 'block' } }} />
implementation="js" 用 useMediaQuery hook 替代:
-<Hidden implementation="js" xlUp><Paper /></Hidden>
+const hidden = useMediaQuery(theme => theme.breakpoints.up('xl'));
+return hidden ? null : <Paper />;
Modal 的 onBackdropClick 属性
已移除,同样改用 onClose 回调(语义与 Dialog 一致,通过 reason === 'backdropClick' 区分背景点击):
function Example() {
const [open, setOpen] = React.useState(false);
const handleClose = (event, reason) => {
if (reason === 'backdropClick') {
// 处理背景点击
}
setOpen(false);
};
return (
<Modal open={open} onClose={handleClose}>
{/* Modal content */}
</Modal>
);
}
Rating 的 MuiRating-readOnly CSS 类
已移除,由全局的 Mui-readOnly 类取代:
-.MuiRating-readOnly
+.Mui-readOnly
StepButtonIcon 类型
已移除,改用 StepButtonProps['icon']:
-import { StepButtonIcon } from '@mui/material/StepButton';
+import { StepButtonProps } from '@mui/material/StepButton';
-StepButtonIcon
+StepButtonProps['icon']
StyledEngineProvider 导入路径
从 '@mui/material' 导入 StyledEngineProvider 已废弃并移除,改从 '@mui/material/styles' 导入:
-import { StyledEngineProvider } from '@mui/material';
+import { StyledEngineProvider } from '@mui/material/styles';
Lab 组件迁入主包
以下 @mui/lab 组件与 hook 已迁入 @mui/material:
- Alert、AlertTitle、Autocomplete、AvatarGroup
- Pagination、PaginationItem、Rating、Skeleton
- SpeedDial、SpeedDialAction、SpeedDialIcon
- ToggleButton、ToggleButtonGroup
- usePagination
继续使用时请改从 @mui/material 导入:
-import Alert from '@mui/lab/Alert';
+import Alert from '@mui/material/Alert';
-import { Alert } from '@mui/lab';
+import { Alert } from '@mui/material';
官方 codemod 自动更新导入:
npx @mui/codemod@latest v7.0.0/lab-removed-components <path/to/folder>
官方警告:该 codemod 不覆盖组件相关的类型导入,类型导入需要手动检查修改。
五、从仓库源码看 v7 codemod 工具集
以上 codemod 命令都由仓库内的 packages/mui-codemod 包提供,v7 版本的全部转换器位于 packages/mui-codemod/src/v7.0.0 目录下,包括:
- grid-props:旧 Grid 属性向 Grid2 语法转换;
- input-label-size-normal-medium:
InputLabel的size="normal"→"medium"; - lab-removed-components:Lab 迁出组件的导入路径重写;
- theme-color-functions:主题颜色函数(
alpha/lighten/darken)相关迁移; - all/v7-all.js:聚合入口,一次性执行整套 v7 转换。
以 lab-removed-components.js 为例,其 REMOVED_EXPORTS 列表(Alert、AlertTitle、Autocomplete、AvatarGroup、Pagination、PaginationItem、Rating、Skeleton、SpeedDial、SpeedDialAction、SpeedDialIcon、ToggleButton、ToggleButtonGroup、usePagination 共 14 项)与官方文档给出的迁移组件清单完全一致;实现上它处理两类导入:@mui/lab/<Component> 的组件文件导入被整体改写为 @mui/material/<Component>,而 @mui/lab 桶导入中被移出的具名绑定会被「搬迁」到 @mui/material 桶导入(若不存在则新建),其余留在 @mui/lab 的具名绑定保持不动。其同目录下的 test-cases 覆盖了组件文件导入、桶导入、混合导入、部分迁移等实际场景,可作为迁移行为可信度的验证依据。每个转换器均配有 .test.js 与 actual/expected 用例,保证转换结果可回归验证。
六、废弃 API 清理:非强制、可分步进行
官方特别说明:升级 v7 不要求你立即处理所有废弃项。这些 v5 起废弃的 API 在 v7 中仍可用,你可以按自己的节奏对照官方的「deprecated APIs 迁移页」逐一清理——但它们将在下一个主版本中被真正移除。因此合理的策略是:先完成本文第四节的破坏性变更迁移,跑通构建与测试,再分批次执行废弃项清理。
七、迁移检查清单
- 将
@mui/material及@mui/icons-material、@mui/system、@mui/lab、@mui/material-nextjs、@mui/styled-engine、@mui/styled-engine-sc、@mui/utils全部升至7.0.0;MUI X 各包保持原版本。 - 确认 TypeScript ≥ 4.9;
@types/react/@types/react-dom主版本与react一致;提交本次改动。 - React ≤ 18 的项目:安装同版本
react-is,并在package.json配置overrides(npm/pnpm)或resolutions(yarn);提交本次改动。 - 清理 webpack/Vite 配置中的
modernalias 与 icons 包 ESM alias;把深层导入(如styles/createTheme)改为一层导入;主题类型增强路径改为@mui/material/styles并核对接口改名(TypographyOptions→TypographyVariantsOptions等)。 - 执行
npx @mui/codemod@latest v7.0.0/grid-props <path>完成 Grid 迁移(或手工改为GridLegacy/Grid命名空间引用,含MuiGrid*主题键与 CSS 类名)。 - 执行
npx @mui/codemod@latest v7.0.0/input-label-size-normal-medium <path>处理size="normal";检查依赖MuiInputLabel-sizeMedium类的自定义样式。 - 执行
npx @mui/codemod@latest v7.0.0/lab-removed-components <path>处理 Lab 组件导入;手动补查类型导入。 - 全局搜索并替换已移除 API:
createMuiTheme、onBackdropClick(Dialog/Modal)、experimentalStyled、Hidden/PigmentHidden、StepButtonIcon、MuiRating-readOnly、TablePagination/TablePaginationActions路径。 - 使用 cssVariables + 双色方案的项目:审查主题切换逻辑,按
theme.vars/color-mix/theme.colorSchemes三种方式改写样式;确需旧行为时给ThemeProvider加forceThemeRerender。 - 跑完整构建、类型检查与测试;最后对照废弃 API 清单制定分阶段清理计划。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00