@mui/codemod 迁移脚本完全指南:从 v0.15 到 v9 一键升级 Material UI、Base UI、MUI System 与 Joy UI
@mui/codemod 是 Material UI 官方开源的自动化迁移工具集,它基于 jscodeshift(改写 JS/TS 源码)并可选地通过 postcss(同步改写 CSS 文件)把历史版本的组件 API 自动升级到新版本,覆盖 Material UI、Base UI、MUI System 与 Joy UI。读完本文,你将掌握 @mui/codemod 的命令行用法、全部选项与自定义包名场景,了解 deprecations/ 下针对最新废弃 API 的迁移集合,以及 v9→v0.15 各历史大版本迁移脚本的适用场景和注意事项,从而在项目升级中安全、批量地完成代码改造。
一、@mui/codemod 是什么
在 Material UI 的历史演进中,多次出现破坏性 API 变更:属性改名、组件迁移包、components/componentsProps 统一为 slots/slotProps、主题函数签名变化、CSS 类名语义拆分等。手工迁移容易遗漏且极耗精力,于是官方把这些一次性改写逻辑封装成一个个 codemod 脚本,发布为独立的 npm 包 @mui/codemod。
其核心特点如下:
- 基于 jscodeshift 对 JavaScript/TypeScript 源码做 AST 变换;
- 部分脚本还会附带 postcss 插件,同步改写项目里的
.css文件(例如拆分后的类名选择器); - 脚本以迁移目标版本 命名并分层存放,历史脚本长期保留,方便任何旧版本项目逐级迁移;
- 仓库内源码位于 packages/mui-codemod/src,按
v0.15.0、v1.0.0、v4.0.0、v5.0.0、v6.0.0、v7.0.0、v9.0.0和deprecations/分目录组织,每个脚本通常配有独立的.test.js与test-cases/夹具用于验证变换正确性; - 依据 packages/mui-codemod/package.json,当前包版本为
9.4.0,要求node >= 20.19.0,核心依赖为jscodeshift@^17.4.0、postcss与yargs。
二、安装与命令行用法
该包无需显式安装,通过 npx 即可直接运行:
npx @mui/codemod@latest <codemod> <paths...>
其中:
codemod:要执行的迁移脚本名称(必填字符串),例如v5.0.0/theme-spacing-api、deprecations/all;paths:透传给jscodeshift的文件或目录路径(必填),可以传一个或多个。
官方 CLI 内建示例:
npx @mui/codemod@latest v4.0.0/theme-spacing-api src
npx @mui/codemod@latest v5.0.0/component-rename-prop src -- \
--component=Grid --from=prop --to=newProp
npx @mui/codemod@latest v5.0.0/preset-safe src --parser=flow
命令行选项
完整选项由 CLI 入口源码 用 yargs 定义,汇总如下:
| 选项 | 含义 | 默认值 |
|---|---|---|
--version |
显示版本号 | false |
--help |
显示帮助 | false |
--dry |
预演模式,不真正改动任何文件 | false |
--parser |
指定 jscodeshift 使用的解析器 | 'tsx' |
--print |
把变换后的文件打印到 stdout(利于开发调试) | false |
--jscodeshift |
(高级)把字符串原样追加给 jscodeshift 进程 | false |
--packageName |
在 import 语句中查找的包名 | '@mui/material' |
从源码可以确认的默认行为细节:
- jscodeshift 阶段默认处理扩展名为
js,ts,jsx,tsx,json的文件,并强制忽略**/node_modules/**与**/*.css(.css交给后续 postcss 阶段); - 若指定的 transform 在源码中找不到,CLI 会抛出类似
Transform '<name>' not found的错误并提示查阅 README; - 实际执行方式是
child_process.spawnSync('node', [jscodeshiftExecutable, '--transform', ...]),并在控制台打印完整命令供排查。
输出与预演
在正式改动前,建议先使用 --dry 观察将要发生的变化:
npx @mui/codemod@latest v7.0.0/grid-props src --dry
配合 --print 可以不改写文件而直接在终端查看每个文件的变换结果,对调试与审查非常有用。
三、自定义包名:--packageName
默认情况下脚本只识别从 @mui/material 导入的组件。如果你的项目使用自定义包名来转发(re-export)Material UI 组件,例如 @org/ui,就需要显式指定:
npx @mui/codemod@latest --packageName="@org/ui"
上面的命令会让脚本在源码里查找 @org/ui 而不是 @mui/material。从 v9.0.0 system-props 实现 可以看到,packageName 会被并入一个 importSources 数组,凡是匹配该字符串本身或以其 / 开头的 import 声明(例如 @org/ui/Button)都会被识别,从而支持路径级导入(import Button from '@mui/material/Button')与命名导入(import { Button } from '@mui/material')两种写法。
四、透传 jscodeshift 与 recast 选项
--jscodeshift
想向底层 jscodeshift 透传更多参数时使用 --jscodeshift="...",字符串会被原样拼接到 jscodeshift 命令行:
npx @mui/codemod@latest --jscodeshift="--run-in-band --verbose=2"
recast printOptions
部分变换需要控制代码打印格式(如引号风格),可以借助 jscodeshift 的 printOptions 命令行参数间接传给 recast 的 printer:
npx @mui/codemod@latest <transform> <path> --jscodeshift="--printOptions='{\"quote\":\"double\"}'"
例如在 removeSystemProps.js 中,options.printOptions 会被取出并传给 root.toSource(printOptions),最终按你的偏好生成输出。
-- 后的 codemod 专用参数
-- 之后的参数不进入 jscodeshift,而是作为该 codemod 自己的输入。典型例子:
npx @mui/codemod@latest v5.0.0/component-rename-prop <path> -- \
--component=Grid --from=prop --to=newProp
它会把任意组件 <Component prop="value" /> 泛化地重命名为 <Component newProp="value" />。还有 v7/v6 的 Grid 脚本接受自定义断点、v9 的 system-props 接受 --jsx,详见下文。
五、JS 与 CSS 双通道改写机制
升级中有一类常见的破坏性变更:把“组合型”状态类名拆成多个独立状态类。例如 .MuiButton-textPrimary 被拆为 .MuiButton-text 与 .MuiButton-colorPrimary 的组合。这同时影响两种载体:
- JS 通道:
MuiButton: { styleOverrides: { root: { '& .MuiButton-textPrimary': {...} } } }或[&.${buttonClasses.textPrimary}]这样的模板字符串; - CSS 通道:项目里真实的
.MuiButton-textPrimary { ... }选择器。
因此对应的 -classes 类脚本在 README 中会并列给出 JS transforms 与 CSS transforms 两段 diff。在底层实现上,codemod.js 的 runPostcssTransform 会先探测 transform 目录下是否存在 postcss.config.js,若存在就把用户传入路径下解析出的 .css 文件(传入目录时为 目录/**/*.css)交给 postcss --replace --verbose 执行;若不存在则静默跳过 postcss 阶段(这是文档中"部分 codemod 会运行 postcss 插件"的实现基础)。
六、deprecations/:面向当前最新废弃 API 的迁移集合
deprecations/ 前缀集中收纳了紧跟最新版本废弃周期的所有迁移脚本,统一入口是:
npx @mui/codemod@latest deprecations/all <path>
all 是全部废弃脚本的组合。这一集合按变更性质可分为两大类,下面逐一说明(每个脚本都对应 src/deprecations 下的同名目录,并可用各自命令行单独执行)。
6.1 属性迁移到 slots / slotProps
Material UI 正在把面向"自定义内部渲染节点"的历史属性(components、componentsProps、XXComponent、XXProps、TransitionComponent、PaperProps 等)统一收敛到 slots 与 slotProps。以下是文档给出的代表性脚本:
Accordion:TransitionComponent/TransitionProps 归入 transition slot
<Accordion
- TransitionComponent={CustomTransition}
- TransitionProps={{ unmountOnExit: true }}
+ slots={{ transition: CustomTransition }}
+ slotProps={{ transition: { unmountOnExit: true } }}
/>
npx @mui/codemod@latest deprecations/accordion-props <path>
Alert:components/componentsProps 迁移为 slots/slotProps(组件用法与 theme defaultProps 都会处理)
<Alert
- components={{ CloseButton: CustomButton }}
- componentsProps={{ closeButton: { testid: 'test-id' } }}
+ slots={{ closeButton: CustomButton }}
+ slotProps={{ closeButton: { testid: 'test-id' } }}
/>
MuiAlert: {
defaultProps: {
- components: { CloseButton: CustomButton }
- componentsProps: { closeButton: { testid: 'test-id' }}
+ slots: { closeButton: CustomButton },
+ slotProps: { closeButton: { testid: 'test-id' } },
},
},
npx @mui/codemod@latest deprecations/alert-props <path>
Autocomplete:多个组件/属性一并收敛,且 renderTags/getTagProps/focusedTag 改名
<Autocomplete
- ChipProps={{ height: 10 }}
- PaperComponent={CustomPaper}
- PopperComponent={CustomPopper}
- ListboxComponent={CustomListbox}
- ListboxProps={{ height: 12 }}
- renderTags={(value, getTagProps, ownerState) =>
- value.map((option, index) => (
- <Chip label={option.label} {...getTagProps({ index })} />
- ))
- }
- componentsProps={{
- clearIndicator: { width: 10 },
- paper: { width: 12 },
- popper: { width: 14 },
- popupIndicator: { width: 16 },
- }}
+ slots={{
+ paper: CustomPaper,
+ popper: CustomPopper
+ }}
+ slotProps={{
+ chip: { height: 10 },
+ listbox: {
+ component: CustomListbox,
+ ...{ height: 12 },
+ },
+ clearIndicator: { width: 10 },
+ paper: { width: 12 },
+ popper: { width: 14 },
+ popupIndicator: { width: 16 },
+ }}
+ renderValue={(value, getItemProps, ownerState) =>
+ value.map((option, index) => (
+ <Chip label={option.label} {...getItemProps({ index })} />
+ ))
+ }
/>
同时该脚本还会处理 useAutocomplete 返回值与 renderInput 内部对 params.InputProps 的访问:
const {
- getTagProps,
- focusedTag,
+ getItemProps,
+ focusedItem,
} = useAutocomplete(props);
npx @mui/codemod@latest deprecations/autocomplete-props <path>
AvatarGroup:slot 名从 additionalAvatar 调整为 surplus
<AvatarGroup
- componentsProps={{
- additionalAvatar: { color: 'red' },
+ slotProps={{
+ surplus: { color: 'red' },
}}
/>
npx @mui/codemod@latest deprecations/avatar-group-props <path>
Checkbox / Radio / Switch:inputProps 与 inputRef 合入 slotProps.input
<Checkbox
- inputProps={{ 'aria-label': 'Checkbox' }}
- inputRef={ref}
+ slotProps={{ input: { 'aria-label': 'Checkbox', ref } }}
/>
npx @mui/codemod@latest deprecations/checkbox-props <path>
npx @mui/codemod@latest deprecations/radio-props <path>
npx @mui/codemod@latest deprecations/switch-props <path>
Drawer(含 SwipeableDrawer)与 Popover、Menu、Dialog:背板/纸面/过渡等统一进 slot
<Drawer
- BackdropComponent={CustomBackdrop}
- BackdropProps={{ transitionDuration: 300 }}
- PaperProps={{ elevation: 20 }}
- SlideProps={{ direction: 'right' }}
+ slots={{ backdrop: CustomBackdrop }}
+ slotProps={{
+ backdrop: { transitionDuration: 300 },
+ paper: { elevation: 20 },
+ transition: { direction: 'right' },
+ }}
/>
npx @mui/codemod@latest deprecations/drawer-props <path>
TextField:五类属性全部并入 slotProps(注意命名语义变化)
<TextField
- InputProps={CustomInputProps}
- inputProps={CustomHtmlInputProps}
- SelectProps={CustomSelectProps}
- InputLabelProps={CustomInputLabelProps}
- FormHelperTextProps={CustomFormHelperProps}
+ slotProps={{
+ input: CustomInputProps,
+ htmlInput: CustomHtmlInputProps,
+ select: CustomSelectProps,
+ inputLabel: CustomInputLabelProps,
+ formHelper: CustomFormHelperProps,
+ }}
/>
npx @mui/codemod@latest deprecations/text-field-props <path>
这一类别下,README 还记录了以下脚本(命令一律为 npx @mui/codemod@latest deprecations/<name> <path>):
| 脚本名 | 迁移要点 |
|---|---|
avatar-props |
imgProps → slotProps.img |
backdrop-props |
components(Root)/componentsProps → slots/slotProps,TransitionComponent → slots.transition |
badge-props |
同上,components/componentsProps → slots/slotProps |
card-header-props |
titleTypographyProps/subheaderTypographyProps → slotProps.title/slotProps.subheader |
dialog-props |
PaperProps→slotProps.paper、TransitionComponent→slots.transition、TransitionProps→slotProps.transition |
filled-input-props、input-base-props、input-props、outlined-input-props |
各类 Input 的 components/componentsProps → slots/slotProps |
form-control-label-props |
componentsProps.typography → slotProps.typography |
list-item-props |
components/componentsProps → slots/slotProps |
list-item-text-props |
primaryTypographyProps/secondaryTypographyProps → slotProps.primary/slotProps.secondary |
menu-props |
TransitionComponent/MenuListProps/TransitionProps → slots/slotProps(list/transition) |
mobile-stepper-props |
LinearProgressProps → slotProps.progress |
modal-props |
components(Root/Backdrop)/componentsProps → slots/slotProps |
pagination-item-props |
components 导航图标 → slots |
popover-props |
Backdrop/Paper/Transition 系列属性 → slots/slotProps |
popper-props |
components(Root)/componentsProps → slots/slotProps |
rating-props |
IconContainerComponent → slots.icon.component |
slider-props |
components(Track)/componentsProps → slots/slotProps |
snackbar-props |
ClickAwayListener/Content/Transition 系列 → slots/slotProps |
speed-dial-props、speed-dial-action-props |
过渡与 Fab/Tooltip 属性 → slots/slotProps |
step-content-props、step-label-props |
Transition/StepIcon/Typography 属性 → slots/slotProps |
tabs-props |
ScrollButtonComponent/TabIndicatorProps/TabScrollButtonProps → slots/slotProps;slots.StartScrollButtonIcon 等改为小驼峰 startScrollButtonIcon |
tooltip-props |
Popper/Transition/components/componentsProps 全量 → slots/slotProps |
6.2 复合状态类名拆分(含 CSS 同步)
第二类废弃是针对"一个类名同时编码多个状态"的历史 CSS 结构。新版把变体类名拆成相互独立的类(如 text+colorSuccess),并让子元素状态类转移为对父级组合类名的后代选择。
以 Button 为例(JS 通道):
import { buttonClasses } from '@mui/material/Button';
MuiButton: {
styleOverrides: {
root: {
- [`&.${buttonClasses.textPrimary}`]: {
+ [`&.${buttonClasses.text}.${buttonClasses.colorPrimary}`]: {
color: 'red',
},
- [`& .${buttonClasses.iconSizeSmall}`]: {
+ [`&.${buttonClasses.sizeSmall} > .${buttonClasses.icon}`]: {
color: 'red',
},
},
},
},
CSS 通道同步改写:
-.MuiButton-textPrimary
+.MuiButton-text.MuiButton-colorPrimary
-.MuiButton-root .MuiButton-iconSizeSmall
+.MuiButton-root.MuiButton-sizeSmall > .MuiButton-icon
npx @mui/codemod@latest deprecations/button-classes <path>
AccordionSummary 同理,把容器上的 contentGutters 组合拆开:
- [`& .${accordionSummaryClasses.contentGutters}`]: {
+ [`&.${accordionSummaryClasses.gutters} .${accordionSummaryClasses.content}`]: {
color: 'red',
},
CSS:
-.MuiAccordionSummary-root .MuiAccordionSummary-contentGutters
+.MuiAccordionSummary-root.MuiAccordionSummary-gutters .MuiAccordionSummary-content
npx @mui/codemod@latest deprecations/accordion-summary-classes <path>
Select 的图标选择器改为兄弟选择器形式:
- [`& .${selectClasses.iconFilled}`]: {
+ [`& .${selectClasses.filled} ~ .${selectClasses.icon}`]: {
color: 'red',
},
npx @mui/codemod@latest deprecations/select-classes <path>
该类目下其余脚本(均同时提供 JS transforms 与 CSS transforms,命令格式相同):
| 脚本名 | 典型拆分语义(举例) |
|---|---|
alert-classes |
standardSuccess → standard + colorSuccess(filled/outlined/standard × success/info/warning/error) |
button-group-classes |
groupedTextHorizontal → text.horizontal 下的 grouped 后代 |
chip-classes |
clickableColorPrimary → clickable + colorPrimary;avatarSmall/iconSmall/deleteIcon... → 尺寸类下的子元素 |
circular-progress-classes |
circleDeterminate/circleIndeterminate → determinate/indeterminate 下的 circle |
dialog-classes |
paperScrollBody/paperScrollPaper → scroll 类下的 > .paper |
drawer-classes |
paperAnchorLeft/paperAnchorDockedLeft 等 → anchorLeft/docked.anchorLeft 下的 > .paper |
image-list-item-bar-classes |
titleWrapBelow/titleWrapActionPosLeft 等 → position/actionPosition 类下的 titleWrap/actionIcon |
input-base-classes |
inputSizeSmall/inputMultiline 等 → 根状态类下的 > .input |
linear-progress-classes |
bar1Buffer/barColorPrimary 等 → 状态类下的 bar1/bar2/bar/dashed |
pagination-item-classes |
textPrimary/outlinedSecondary 等 → text/outlined + colorXxx |
slider-classes |
thumbSizeSmall/thumbColorPrimary 等 → size/color 类下的 > .thumb |
step-connector-classes |
lineHorizontal/lineVertical → horizontal/vertical 下的 line |
tab-classes |
iconWrapper 更名为 icon |
table-sort-label-classes |
iconDirectionAsc/iconDirectionDesc → direction 类下的 > .icon |
toggle-button-group-classes |
groupedHorizontal/groupedVertical → 方向类下的 grouped |
6.3 属性删除或迁移到 sx
另一些废弃属性被直接移除并建议改用 sx 表达。典型示例:
Divider 的 light
<Divider
- light
+ sx={{ opacity: 0.6 }}
/>
npx @mui/codemod@latest deprecations/divider-props <path>
Typography 的 paragraph
<Typography
- paragraph
+ sx={{ marginBottom: '16px' }}
/>
<Typography
- paragraph={isTypographyParagraph}
+ sx={isTypographyParagraph ? { marginBottom: '16px' } : undefined}
/>
MuiTypography: {
defaultProps: {
- paragraph: true
+ sx: { marginBottom: '16px' },
},
},
npx @mui/codemod@latest deprecations/typography-props <path>
七、版本化迁移脚本纵览
7.1 v9.0.0/system-props
npx @mui/codemod@latest v9.0.0/system-props <path>
把 Box、Stack、Typography、Link、Grid、DialogContentText、TimelineContent、TimelineOppositeContent 上的系统属性(布局、间距、颜色、字体等)搬进 sx。相比 v6 版本,v9 版本还额外处理:
- Typography 的
color="inherit"(移入sx); - Link 的
color="text.secondary"(移入sx,同时保留"primary"、"inherit"这类具名颜色作为组件属性); - DialogContentText、TimelineContent、TimelineOppositeContent 组件。
-<Typography color="inherit" />
+<Typography sx={{ color: "inherit" }} />
-<Link color="text.secondary" href="#" />
+<Link href="#" sx={{ color: "text.secondary" }} />
实现上(见 removeSystemProps.js),脚本内部维护了一份从 @mui/system 的 defaultSxConfig 派生出的系统属性名单(border、p/margin、display、flexbox、grid、position、shadow、sizing、typography 等约百项),先通过 import 扫描识别目标组件,再把 JSX 属性与既有 sx 智能合并(对象合并、数组前置、展开符处理等),并对颜色类组件使用单独的"匹配器"决定哪些颜色值保留为属性。
处理无显式 import 的项目(--jsx):如果项目使用 unplugin-auto-import 这类自动导入插件,组件可能没有显式 import 语句,此时可跳过 import 检测、直接用 --jsx 指定元素名:
npx @mui/codemod@latest v9.0.0/system-props <path> -- --jsx=Box,Typography,Stack,Link,Grid,DialogContentText
7.2 v7.0.0
theme-color-functions:把从 @mui/system/colorManipulator 导入的 alpha()/lighten()/darken() 换成 theme 上的方法:
- import { alpha, lighten, darken } from '@mui/system/colorManipulator';
- alpha(theme.palette.primary.main, 0.8)
+ theme.alpha((theme.vars || theme).palette.primary.main, 0.8)
- lighten(theme.palette.primary.main, 0.1)
+ theme.lighten(theme.palette.primary.main, 0.1)
- darken(theme.palette.primary.main, 0.3)
+ theme.darken(theme.palette.primary.main, 0.3)
npx @mui/codemod@latest v7.0.0/theme-color-functions <path>
grid-props:更新 @mui/material/Grid、@mui/system/Grid、@mui/joy/Grid 的响应式尺寸/偏移写法:
<Grid
- xs={12}
- sm={6}
- xsOffset={2}
- smOffset={3}
+ size={{ xs: 12, sm: 6 }}
+ offset={{ xs: 2, sm: 3 }}
/>
可通过 --jscodeshift='--muiBreakpoints=mobile,desktop' 传入主题自定义断点,让变换使用项目自己的断点名:
npx @mui/codemod@latest v7.0.0/grid-props <path> --jscodeshift='--muiBreakpoints=mobile,desktop'
- <Grid mobile={12} mobileOffset={2} desktop={6} desktopOffset={4} >
+ <Grid size={{ mobile: 12, desktop: 6 }} offset={{ mobile: 2, desktop: 4 }} >
lab-removed-components:修正一批从 @mui/lab 移入 @mui/material 的组件/hook 的 import,包括 Alert、AlertTitle、Autocomplete、AvatarGroup、Pagination、PaginationItem、Rating、Skeleton、SpeedDial、SpeedDialAction、SpeedDialIcon、ToggleButton、ToggleButtonGroup、usePagination。既处理 @mui/lab 顶层命名导入,也处理组件级文件导入:
- import { Alert } from '@mui/lab';
+ import { Alert } from '@mui/material';
- import Alert, { alertClasses } from '@mui/lab/Alert';
+ import Alert, { alertClasses } from '@mui/material/Alert';
npx @mui/codemod@latest v7.0.0/lab-removed-components <path>
input-label-size-normal-medium:把 InputLabel 的 size 取值 "normal" 改为 "medium":
-<InputLabel size="normal">Label</InputLabel>
+<InputLabel size="medium">Label</InputLabel>
npx @mui/codemod@latest v7.0.0/input-label-size-normal-medium <path>
7.3 v6.0.0
sx-prop:把 sx 中的函数写法改写为与 @pigment-css/react 兼容的形式——将 theme.palette.mode 条件判断替换为 theme.applyStyles():
<Box
- sx={{
- backgroundColor: (theme) =>
- theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900],
- }}
+ sx={theme => ({
+ backgroundColor: theme.palette.grey[900],
+ ...theme.applyStyles("light", {
+ backgroundColor: theme.palette.grey[100]
+ })
+ })}
/>
npx @mui/codemod@latest v6.0.0/sx-prop <path>
system-props:移除 Box 等的系统属性并放入 sx:
-<Box ml="2px" py={1} color="primary.main" />
+<Box sx={{ ml: '2px', py: 1, color: 'primary.main' }} />
npx @mui/codemod@latest v6.0.0/system-props <path>
theme-v6:把 @mui/system@v5 的主题创建升级到与 Pigment CSS 兼容:用 theme.applyStyles() 替换 palette mode 条件;用 variants 替换 ownerState 分支;把主题 variants 移到根 slot:
createTheme({
components: {
MuiButton: {
- variants: [ { props: { color: 'primary' }, style: { color: 'red' } } ],
styleOverrides: {
- root: ({ theme, ownerState }) => ({
+ root: ({ theme }) => ({
...ownerState.variant === 'contained' && {
backgroundColor: alpha(theme.palette.primary.main, 0.8),
...theme.palette.mode === 'dark' && { ... }
},
+ variants: [
+ { prop: { variant: 'contained' }, style: { ... } },
+ { prop: { color: 'primary' }, style: { color: 'red' } },
+ ],
})
}
}
}
})
npx @mui/codemod@latest v6.0.0/theme-v6 <path>
styled:把 @mui/system@v5 风格、基于 props 的 styled 样式改写为 variants:
styled('div')(({ theme, disabled }) => ({
color: theme.palette.primary.main,
- ...(disabled && {
- opacity: 0.5,
- }),
+ variants: [
+ {
+ prop: 'disabled',
+ style: { opacity: 0.5 },
+ },
+ ],
}));
npx @mui/codemod@latest v6.0.0/styled <path>
该脚本能处理含展开符、三元表达式与嵌套对象的复杂样式,但文档明确列出两类局限性,需要人工兜底:
-
动态值不转换:如
width: ownerState.width ?? '100%',建议手动声明 CSS 变量并用内联样式赋值:const ResizableContainer = styled('div')({ width: 'var(--ResizableContainer-width, 100%)', height: 'var(--ResizableContainer-height, 100%)', }); -
动态引用主题不转换:如
backgroundColor: (theme.vars || theme).palette[ownerState.color]?.main,需要手动遍历主题为每个颜色生成variants:const Test = styled('div')(({ theme }) => ({ variants: Object.entries(theme.palette) .filter(([color, value]) => value.main) .map(([color, value]) => ({ props: { color }, style: { backgroundColor: value.main }, })), }));
grid-v2-props:针对 @mui/material/Grid2、@mui/system/Grid、@mui/joy/Grid 的 size/offset 迁移,行为与 v7 的 grid-props 一致,同样支持 --muiBreakpoints:
npx @mui/codemod@latest v6.0.0/grid-v2-props <path>
7.4 v5.0.0:史上最大规模的一批迁移脚本
v5 迁移脚本数量最多,其中 preset-safe 是"一键组合",官方提示它包含所有重要变换器、只能运行一次:
npx @mui/codemod@latest v5.0.0/preset-safe <path|folder>
preset-safe 的组合清单见 README 与 src/v5.0.0/preset-safe.js,涵盖下列脚本中的大部分。下面按用途分类说明这些独立脚本。
包名与模块路径迁移(改名 @material-ui/* → @mui/*、模块搬迁)
| 脚本 | 作用 |
|---|---|
mui-replace |
全局替换包名:`@material-ui/core |
moved-lab-modules |
把已从 lab 移入 core 的组件 import 改到 @material-ui/core(Skeleton、SpeedDial、Alert、Autocomplete、Pagination、ToggleButton 等) |
date-pickers-moved-to-x |
日期/时间选择器 import 由 @mui/lab 改为 @mui/x-date-pickers、@mui/x-date-pickers-pro |
tree-view-moved-to-x |
Tree View import 由 @mui/lab 改为 @mui/x-tree-view |
optimal-imports |
把过深的私有导入收敛到公开入口,利于 tree shaking |
path-imports |
顶层命名导入转路径导入(top-level-imports 的逆操作,并增加 @mui/icons-material) |
top-level-imports |
@mui/material 子模块导入合并为根模块命名导入 |
core-styles-import |
把 core/styles/* 私有路径导入改到 core/styles |
material-ui-styles |
把 JSS 相关导出从 @material-ui/core/styles 拆分到 @material-ui/styles |
Base UI / Joy UI 专属
| 脚本 | 作用 |
|---|---|
base-use-named-exports |
Base UI 默认导出改具名导出(import/reexport 同步更新) |
base-remove-unstyled-suffix |
去掉组件/类型名中的 Unstyled 后缀 |
base-remove-component-prop |
移除 Base UI 的 component prop,值转移到 slots.root |
base-hook-imports |
hooks 的导入路径改为新目录 |
base-rename-components-to-slots |
Base UI 的 components/componentsProps → slots/slotProps,slot 字段改小驼峰 |
rename-css-variables |
Joy UI CSS 变量命名规范化,例如 --List-divider-gap → --ListDivider-gap、--Switch-track-width → --Switch-trackWidth |
joy-rename-classname-prefix |
Joy UI 类名前缀 Joy → Mui |
joy-rename-row-prop |
Card/List/RadioGroup 的 row → orientation="horizontal" |
joy-avatar-remove-imgProps |
Joy Avatar 的 imgProps 并入 slotProps.img |
joy-text-field-to-input |
Joy <TextField> 拆成 FormControl + FormLabel + Input + FormHelperText 组合 |
joy-rename-components-to-slots |
Joy UI 的 components/componentsProps → slots/slotProps |
主题 API 迁移
| 脚本 | 作用 |
|---|---|
create-theme |
createMuiTheme() 改名 createTheme() |
adapter-v4 |
引入 adaptV4Theme 并包裹 createTheme(),衔接 v4 主题结构 |
theme-augment |
为 TS 项目补充 DefaultTheme 模块增强 |
theme-options |
ThemeOptions 类型改名 DeprecatedThemeOptions |
theme-palette-mode |
palette type → mode(对象与 theme.palette.type 访问) |
theme-provider |
MuiThemeProvider 改名 ThemeProvider |
theme-breakpoints |
修正 down('sm')/between('sm','md') 断点语义(⚠️ 非幂等,只能运行一次) |
theme-breakpoints-width |
theme.breakpoints.width('md') → theme.breakpoints.values.md |
theme-spacing |
去掉 theme.spacing(n) 拼接字符串中的多余 px |
theme-typography-round |
theme.typography.round($n) → Math.round($n * 1e5) / 1e5 |
transitions |
transitions 导入改名 createTransitions |
material-ui-types |
Omit 类型改名 DistributiveOmit |
组件属性/样式迁移
| 脚本 | 作用 |
|---|---|
component-rename-prop |
泛化 prop 改名器(--component/--from/--to) |
autocomplete-rename-closeicon |
closeIcon → clearIcon |
autocomplete-rename-option |
getOptionSelected → isOptionEqualToValue |
avatar-circle-circular、pagination-round-circular、skeleton-variant、fab-variant、circularprogress-variant |
变体/形状取值统一,如 circle→circular、round→circular、rect→rectangular、static→determinate |
badge-overlap-value |
`overlap="circle |
box-borderradius-values |
borderRadius="borderRadius"/{16} 等旧语义值改为 v5 的 1/"16px" |
box-rename-css |
Box 的 css prop → sx |
box-rename-gap |
gridGap/gridColumnGap/gridRowGap → gap/columnGap/rowGap |
box-sx-prop |
支持的 Box 系统属性移入 sx |
button-color-prop |
删除 color="default" |
chip-variant-prop |
删除 variant="default" |
collapse-rename-collapsedheight |
collapsedHeight → collapsedSize,classes.container → classes.root |
dialog-props |
移除 disableBackdropClick |
dialog-title-props |
移除 disableTypography |
expansion-panel-component |
ExpansionPanel* → Accordion* 组件改名 |
grid-justify-justifycontent |
justify → justifyContent |
grid-list-component |
GridList* → ImageList* |
hidden-down-props |
Hidden 相关迁移(源码位于 src/v5.0.0) |
icon-button-size |
未指定 size 的 IconButton 补 size="large" 保持 v4 外观 |
link-underline-hover |
未指定 underline 的 Link 补 underline="hover" |
modal-props |
移除 disableBackdropClick、onEscapeKeyDown |
root-ref |
整体移除 RootRef |
styled-engine-provider |
给包含 ThemeProvider 的文件包上 StyledEngineProvider |
table-props |
Table 系列改名:onChangeRowsPerPage→onRowsPerPageChange、onChangePage→onPageChange、padding="default"→"normal"、classes.input→classes.select |
tabs-scroll-buttons |
`scrollButtons="on |
textarea-minmax-rows |
TextField/TextareaAutosize 的 rowsMin/rowsMax → minRows/maxRows,rows 收敛到 TextareaAutosize 的 minRows |
use-autocomplete |
useAutocomplete 的 lab import 改 core |
use-transitionprops |
Dialog/Menu/Popover/Snackbar 的 onEnter*/onExit* 收拢进 TransitionProps |
variant-prop |
TextField/Select/FormControl 未指定 variant 时补 variant="standard";⚠️ 若你已在主题 defaultProps 中设置过 outlined/filled,不要运行它 |
with-mobile-dialog |
移除 withMobileDialog import,插入硬编码回退(含 // FIXME 注释),避免应用崩溃 |
with-width |
移除 withWidth import,插入硬编码回退 |
JSS → styled / tss-react(两条耗时的样式迁移路线)
-
jss-to-styled:把makeStyles/withStyles的 JSS 写法改写成styledAPI,示例中会把首个返回元素替换为styled组件并生成 PREFIX 化的类名常量。文档附注:这种方式把返回语句的第一个元素转成 styled 组件,会提升 CSS 特异性,建议在解决全部破坏性变更之后再执行。npx @mui/codemod@latest v5.0.0/jss-to-styled <path> -
jss-to-tss-react:迁移到等价的tss-react/muiAPI。它未处理以下场景,会在对应位置留下"TODO jss-to-tss-react codemod"注释,需人工跟进:makeStyles返回的 hook(如useStyles)被导出并在其他文件使用时,其他文件的用法不会被转换;- CSS 属性值位置的箭头函数不会被转换(规则级箭头函数支持,但有条件);
- 规则级箭头函数要求参数使用对象解构(如
root: ({ color, padding }) => (...)),未解构的参数(如(props) => ...)不转换; - 规则级箭头函数若包含显式
return的代码块而非对象表达式,不转换。
npx @mui/codemod@latest v5.0.0/jss-to-tss-react <path>
7.5 v4.0.0
| 脚本 | 作用 |
|---|---|
theme-spacing-api |
theme.spacing.unit * x → theme.spacing(x);会做基础表达式化简(乘除换算),但超过一个运算的表达式化简不完善,例如 theme.spacing.unit * 5 * 5 只变成 theme.spacing(5) * 5 |
optimal-imports |
把超过一层的 @material-ui/core 深路径导入收敛为利于 tree shaking 的公开导入(v4 语义) |
top-level-imports |
@material-ui/core 子模块导入合并到根模块(v4 语义) |
npx @mui/codemod@latest v4.0.0/theme-spacing-api <path>
7.6 v1.0.0
| 脚本 | 作用 |
|---|---|
import-path |
适配 v1.0.0 扁平化组件导入路径:@material-ui/core/Menu 的 MenuItem 改为 @material-ui/core/MenuItem。注意:从 pre-v1.0 迁移时需先手工把 material-ui/* 全部替换为 @material-ui/core/* 再运行 |
color-imports |
颜色调色板改新位置,如 blue、teal500 → @material-ui/core/colors/blue 与 teal['500']。支持附加选项:-- --importPath='mui/styles/colors' --targetPath='mui/colors' |
svg-icon-imports |
material-ui/svg-icons/<category>/<icon-name> → @material-ui/icons/<IconName> |
menu-item-primary-text |
<MenuItem primaryText="Profile" /> → <MenuItem>Profile</MenuItem> |
npx @mui/codemod@latest v1.0.0/import-path <path>
npx @mui/codemod@latest v1.0.0/color-imports <path> -- --importPath='mui/styles/colors' --targetPath='mui/colors'
7.7 v0.15.0
import-path 适配 v0.15.0 重新组织的目录结构:源码导入 material-ui/src/flat-button → material-ui/src/FlatButton;npm 导入 material-ui/lib/raised-button → material-ui/RaisedButton。
npx @mui/codemod@latest v0.15.0/import-path <path>
八、完整迁移路径的建议顺序
版本化脚本按目录命名,天然给出了一条逐级升级的主线:项目处于哪个大版本,就按 v0.15.0 → v1.0.0 → v4.0.0 → v5.0.0 → v6.0.0 → v7.0.0 的顺序执行对应脚本;v9 与 deprecations/ 则用于处理最新主版本之后的持续废弃。各版本破坏性变更的详细说明可以在仓库的 docs/data/material-ui/migration 迁移指南目录中查找,与 codemod 一一对应。
实操建议:
- 迁移前先把代码提交为干净基线(或生成 patch),便于审查与回滚;
- 先用
--dry(必要时加--print)检查将产生的改动是否符合预期; - 官方对部分脚本给出"仅可运行一次"警告:
v5.0.0/preset-safe、v5.0.0/theme-breakpoints均非幂等,重复执行可能产生错误结果; - 留意脚本的适用范围标注:
base-*、joy-*只影响对应 UI 体系的组件;variant-prop等对主题默认值有前提假设; --packageName适用于封装了二次转发包名的企业级项目;--parser=flow等参数可按项目语言切换到 Flow/TS 解析器。
九、如何运行包内测试验证迁移器
每个 codemod 都配套了完整的单元测试与"输入/期望输出"夹具。以 v5 脚本为例,src/v5.0.0/ 下每个 .js 变换旁都有同名 .test.js 与同名 .test/ 目录,存放 actual//expected/ 之类的待变换源码与期望产物;v9 与 deprecations/ 中则使用 *.test.js 与 test-cases/ 目录。可在包内直接跑测试验证:
# 在仓库根目录(pnpm workspace)运行 @mui/codemod 的全部单测
pnpm --workspace-root test:unit --project "*:@mui/codemod"
参见 packages/mui-codemod/package.json 的 test 脚本定义。若要本地构建发布产物,可执行 pnpm build(其构建命令会忽略 **/*.test/**、actual.js、expected.js 与 test-cases/** 等测试夹具,只产出可发布的 CJS bundle)。
十、小结
@mui/codemod 把 MUI 家族横跨十余个大版本、数百条破坏性变更沉淀为可重复执行的 AST 变换:deprecations/all 面向最新废弃周期,v9.0.0 起则逐步兼容 Pigment CSS 的 sx/variants/applyStyles 体系,而 v7、v6、v5、v4、v1、v0.15 目录完整保留了历史升级路径。使用前先确认脚本适用范围与"一次性运行"类警告,善用 --dry、--packageName 与 -- 透传参数,再配合仓库内每个脚本自带的测试夹具校验结果,即可把 MUI 升级这类高风险的大规模改动变成可控、可审计的机械流程。
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 StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00