首页
/ MUI System 上手指南:用 `sx` prop 快速构建自定义设计的 CSS 工具集

MUI System 上手指南:用 `sx` prop 快速构建自定义设计的 CSS 工具集

2026-09-06 18:03:24作者:秋阔奎Evelyn

MUI System 是 Material UI 生态中一套独立的 CSS 工具库,它以 sx prop 为核心,让你能直接在组件内部书写“主题感知”的行内样式,快速完成一次性、自定义化的界面布局与视觉定制。本指南基于仓库中的 System 概览文档,结合其姊妹篇 Usage 文档the sx prop 文档Installation 文档,并深入 packages/mui-system 源码,讲清 MUI System 的定位、核心机制、源码实现与最佳使用场景。读完你将掌握:用 sx 在一行内完成主题化样式、按断点响应式取值、把样式能力注入自定义组件,以及在何种取舍下决定是否采用它。

MUI System 是什么

MUI System 是一组 CSS 工具函数(CSS utilities),目标是帮助开发者“更高效地构建自定义设计”,快速完成自定义布局。它不是独立的组件框架,而是被 Material UI 等库内部使用的基础设施——在本仓库 monorepo 中,其源码位于 packages/mui-system/src,Material UI 所在的 packages/mui-material 正是建立在这一层能力之上的产品库。

MUI System 对外提供了若干灵活、通用的包装型组件,例如 BoxContainer,它们可以通过 sx prop 被快速定制。sx 让你把样式直接写在组件自身内部,而不是像 styled-components 那样为每个小改动单独创建繁冗的 const 定义。同时,sx 允许你直接读取主题中自定义的设计令牌(design tokens),从而保证“一次性样式”也能与全局设计体系保持一致性。

System 入口文件 可以看到它的能力全貌:不仅导出 BoxContainerGridStack 等布局组件,还导出 ThemeProviderstyledcreateThemecreateBreakpointsuseThemeuseMediaQuery 等一套完整的主题与样式基础设施,以及 bordersdisplayflexboxpalettespacingtypography 等风格函数。整套风格函数最终被 sx prop 统一封装。

MUI System 的四大优势

官方 Overview 文档将它的价值浓缩为四点,这四点也正是它与 styled-components 这类方案对比时的核心差异:

1. 写更少的代码

在 styled-components 显得“杀鸡用牛刀”的场景里,sx prop 可以把几十行样式定义压缩到组件内部寥寥几行。对于一次性、不打算复用的定制外观,这能显著降低代码噪音。

2. 写你已经会的 CSS

sx prop 是 CSS 的超集:标准 CSS 属性、选择器、伪类、媒体查询全部可用,额外还叠加了若干 MUI 特有的“主题感知”属性。只要熟悉 CSS,几乎零学习成本即可上手。

3. 避免上下文切换

用 styled-components 时,你需要在“使用处”和“定义处”之间来回跳转才能理解一个组件的全貌。而 MUI System 把样式和使用放在同一个 JSX 位置,心智负担更低。

4. 忘掉无谓的命名

给一个样式组件起名往往令人头疼(StatWrapperStatHeaderStyledTrend……)。使用 sx 时,这个命名步骤被直接省略。

从 styled-components 到 sx 的直观对比

Usage 文档 用同一个“数据统计卡片”给出了两种写法的完整对照,其演示代码可在此仓库直接查看:Why.js

方案一:使用 styled-components API

const StatWrapper = styled('div')(
  ({ theme }) => `
  background-color: ${theme.palette.background.paper};
  box-shadow: ${theme.shadows[1]};
  border-radius: ${theme.shape.borderRadius}px;
  padding: ${theme.spacing(2)};
  min-width: 300px;
`,
);

const StatHeader = styled('div')(
  ({ theme }) => `
  color: ${theme.palette.text.secondary};
`,
);

const StyledTrend = styled(TrendingUpIcon)(
  ({ theme }) => `
  color: ${theme.palette.success.dark};
  font-size: 16px;
  vertical-align: sub;
`,
);

const StatValue = styled('div')(
  ({ theme }) => `
  color: ${theme.palette.text.primary};
  font-size: 34px;
  font-weight: ${theme.typography.fontWeightMedium};
`,
);

const StatDiff = styled('div')(
  ({ theme }) => `
  color: ${theme.palette.success.dark};
  display: inline;
  font-weight: ${theme.typography.fontWeightMedium};
  margin-left: ${theme.spacing(0.5)};
  margin-right: ${theme.spacing(0.5)};
`,
);

const StatPrevious = styled('div')(
  ({ theme }) => `
  color: ${theme.palette.text.secondary};
  display: inline;
  font-size: 12px;
`,
);

return (
  <StatWrapper>
    <StatHeader>Sessions</StatHeader>
    <StatValue>98.3 K</StatValue>
    <StyledTrend />
    <StatDiff>18.77%</StatDiff>
    <StatPrevious>vs last week</StatPrevious>
  </StatWrapper>
);

方案二:使用 MUI System 的 sx

<Box
  sx={{
    bgcolor: 'background.paper',
    boxShadow: 1,
    borderRadius: 1,
    p: 2,
    minWidth: 300,
  }}
>
  <Box sx={{ color: 'text.secondary' }}>Sessions</Box>
  <Box sx={{ color: 'text.primary', fontSize: 34, fontWeight: 'medium' }}>
    98.3 K
  </Box>
  <Box
    component={TrendingUpIcon}
    sx={{ color: 'success.dark', fontSize: 16, verticalAlign: 'sub' }}
  />
  <Box
    sx={{ color: 'success.dark', display: 'inline', fontWeight: 'medium', mx: 0.5 }}
  >
    18.77%
  </Box>
  <Box sx={{ color: 'text.secondary', display: 'inline', fontSize: 12 }}>
    vs. last week
  </Box>
</Box>

注意几个信息量很大的细节:

  • bgcolor: 'background.paper'color: 'text.secondary'主题调色板路径,而非普通 CSS 值;
  • boxShadow: 1 等价于 theme.shadows[1]
  • borderRadius: 1 会乘以 theme.shape.borderRadius
  • p: 2mx: 0.5 会自动换算为 theme.spacing(...)
  • component={TrendingUpIcon}Box 直接渲染为图标组件,这是 Box 作为可覆盖组件(overridable component)的能力。

同样是“复用主题令牌”,前者每处都要写 ({ theme }) => ... 与模板字符串,后者则是纯声明式对象。

sx 可以在哪些位置使用

Usage 文档 指出,sx prop 可在四个位置使用:

  1. 核心组件(Core components):Material UI 的所有组件(Button、Card、Typography……)都原生支持 sx prop;
  2. Box 包装器Box 本身是轻量组件,默认渲染为 <div>,是 sx 最直接的使用载体,同时可以充当其他组件的包装层;
  3. 自定义组件(Custom components):通过 @mui/material/styles(或 MUI System 自己的 styled)把 sx 能力接入自定义组件:
import { styled } from '@mui/material/styles';

const Div = styled('div')``;

更完整的做法(包括把 sx 从外层组件安全地传递给内部 System 组件、以及通过 StyleFunctionSx 类型组合风格函数)可参阅 custom-components.md; 4. 任意元素(配合 Babel 插件):在官方对 sx Babel 插件的探讨落地之前,此方案仍处于社区讨论阶段,实战中通常优先使用前三种位置。

组件 props 与 sx 的分工

在 API 设计上,MUI 组件把“影响组件行为”的 props(如 Button 的 color 会同时影响 hover、focus 等多重状态)与“纯 CSS 效果”的 sx 分离开。系统属性统一经由 sx 注入,组件 props 则聚焦于有文档定义的行为,从而避免与原生及自定义 props 产生冲突——这是 Usage 文档 特别强调的 API 取舍。

sx 的语义:CSS 超集 + 主题令牌映射

the sx prop 文档 定义:sx 是“访问主题的快捷样式写法”,它把 @mui/system 暴露的全部风格函数打包进一个 prop。sx 的 key 未必是合法 CSS 属性,因为其中很多会被映射到主题对象的具体字段。映射逻辑的“字典”就是源码中的 defaultSxConfig.ts,其核心配置项结构为:

export interface SxConfigRecord {
  cssProperty?: keyof React.CSSProperties | false | undefined; // 输出到哪个 CSS 属性
  themeKey?: string | undefined;                                // 在 theme 中的点路径,如 'palette'
  transform?: TransformFunction | undefined;                    // 自定义换算函数
  style?: SimpleStyleFunction<any> | undefined;                 // 复用的风格函数
}

典型的主题映射速查

sx 写法 实际效果 配置出处(defaultSxConfig.ts)
border: 1 border: '1px solid black'(数字仅表示像素宽度,颜色固定为黑色) border 项配 borderTransform
borderColor: 'primary.main' border-color: theme.palette.primary.main themeKey: 'palette'
borderRadius: 2 border-radius: 2 * theme.shape.borderRadius themeKey: 'shape.borderRadius'style: borderRadius
color: 'primary.main' color: theme.palette.primary.main themeKey: 'palette'
bgcolor: 'primary.main' backgroundColor,同 palette 路径 单独配置 cssProperty: 'backgroundColor'
m: 2 / p: 2 margin/padding: theme.spacing(2) 复用的 margin / padding 风格函数
zIndex: 'tooltip' z-index: theme.zIndex.tooltip themeKey: 'zIndex'
boxShadow: 1 box-shadow: theme.shadows[1] themeKey: 'shadows'
fontWeight: 'light' font-weight: theme.typography.fontWeightLight themeKey: 'typography'
typography: 'body1' 展开为 { ...theme.typography.body1 } cssProperty: falsethemeKey: 'typography'
displayPrint: 'none' @media print { display: none } cssProperty: false + transform

spacing 方向别名

spacing 组是最常用的速记,官方为其提供了一整套方向别名(详见 Spacing 页 同目录风格文档与 the-sx-prop.md):

Prop CSS property
m margin
mt / mr / mb / ml margin-top / margin-right / margin-bottom / margin-left
mx margin-leftmargin-right
my margin-topmargin-bottom
p padding
pt / pr / pb / pl padding-top / padding-right / padding-bottom / padding-left
px padding-leftpadding-right
py padding-toppadding-bottom

在源码层面,defaultSxConfig.tsmmtmxppx 等别名均指向复用的 margin/padding 风格函数;除此之外还支持 paddingXpaddingInlinemarginBlock 等更细粒度的逻辑属性别名。

sizing 的百分比换算

宽度/高度类属性采用了一个特殊换算:当值在 (0, 1] 区间内时转换为百分比,否则按原样输出(如 width: 20 会被当作像素)。换算函数如下:

function transform(value) {
  return value <= 1 && value !== 0 ? `${value * 100}%` : value;
}

因此 sx={{ width: 1 / 2 }} 等价于 width: '50%',而 sx={{ width: 20 }} 等价于 width: '20px'

sx 支持完整 CSS:伪类、媒体查询与嵌套选择器

[usage.md](https://gitcode.com/GitHub_Trending/ma/material-ui/blob/95f68f3eb2fc42dcccf1ba2a0c4c956b0f22e452/docs/data/system/getting-started/usage/usage.md?utm_source=gitcode_repo_files) 给出了三种典型写法:

// 伪选择器
<Box
  sx={{
    ':hover': {
      boxShadow: 6,
    },
  }}
/>

// 媒体查询
<Box
  sx={{
    '@media print': {
      width: 300,
    },
  }}
/>

// 嵌套子选择器
<Box
  sx={{
    '& .ChildSelector': {
      bgcolor: 'primary.main',
    },
  }}
/>

除了这些“对象嵌套”,sx 还接受函数值(访问任意主题字段)和数组(按索引合并、后者优先级更高,便于条件覆盖样式)。当你想整体取用 theme 中某个对象时,the-sx-prop.md 推荐把回调作为整个 sx 值传入:

<Box
  sx={(theme) => ({
    ...theme.typography.body,
    color: theme.palette.primary.main,
  })}
/>

响应式取值:对象、数组与自定义断点

sx 大幅简化了响应式断点的书写。仓库演示位于 BreakpointsAsObjectBreakpointsAsArray 等文件。

对象形式(推荐)

以断点名为 key,且某个断点下的属性对更大的断点同样生效

<Box
  sx={{
    width: {
      xs: 100, // theme.breakpoints.up('xs')
      md: 200, // theme.breakpoints.up('md')
    },
  }}
>

width: { md: 200 } 语义上等价于 theme.breakpoints.up('md')

数组形式

从最小到最大断点依次排列,可用 null 跳过中间断点:

<Box sx={{ width: [null, null, 300] }}>This box has a responsive width.</Box>

文档建议:仅在主题断点很少(例如 3 个)时考虑数组形式,断点较多时优先用对象 API。

容器查询(v6+)

从 v6 起,断点对象支持以 @ 开头的容器查询简写,语法为 @{breakpoint}/{container}

  • breakpointpx 数值、断点 key(默认主题为 sm/md/lg/xl)或合法 CSS 值(如 40em);
  • container(可选):具名 containment context。

容器查询有浏览器兼容性门槛,使用前应先确认目标浏览器支持情况。

自定义断点

usage.md 提供了一个完整示例,可自由定义业务断点名(如 mobile/tablet/laptop/desktop):

import * as React from 'react';
import Box from '@mui/material/Box';
import { createTheme, ThemeProvider } from '@mui/material/styles';

const theme = createTheme({
  breakpoints: {
    values: {
      mobile: 0,
      tablet: 640,
      laptop: 1024,
      desktop: 1280,
    },
  },
});

export default function CustomBreakpoints() {
  return (
    <ThemeProvider theme={theme}>
      <Box
        sx={{
          width: {
            mobile: 100,
            laptop: 300,
          },
        }}
      >
        This box has a responsive width
      </Box>
    </ThemeProvider>
  );
}

TypeScript 项目需要配合模块扩充(module augmentation),把新断点注入主题类型,同时可以关闭默认断点:

declare module '@mui/material/styles' {
  interface BreakpointOverrides {
    xs: false; // 移除 xs 断点
    sm: false;
    md: false;
    lg: false;
    xl: false;
    tablet: true; // 新增 tablet 断点
    laptop: true;
    desktop: true;
  }
}

源码视角:sx 究竟是如何被解析的

sx 并不是魔法。真正的解析核心位于 styleFunctionSx.js,其中最关键的执行流程可以概括为:

  1. 前置短路:若 props.sx 不存在则直接返回 null
  2. 选定配置字典theme.unstable_sxConfig ?? defaultSxConfig——也就是说,主题可以注入自定义的 unstable_sxConfig 覆盖默认的 defaultSxConfig.ts,从而扩展自定义属性映射;
  3. 统一求值:若 sx 是函数则用 theme 调用它得到对象;若是数组则逐项递归处理;
  4. 逐 key 分发:对每个 styleKey,如果存在对应配置(如 themeKeytransformstyle),则走 setThemeValue 完成“主题令牌 → CSS”的换算,并自动套用 spacing 单位换算、调色板路径解析、数值百分比化等 transform;如果 value 恰好命中已定义断点的 key 结构,则走 iterateBreakpoints 展开为媒体查询;
  5. 嵌套递归:对子选择器/伪类等对象值,通过带 nested: true 的 wrapper 递归调用 styleFunctionSx 自身,实现任意深度的嵌套;
  6. 收尾清理:用 removeUnusedBreakpoints 去除未使用的断点容器,用 sortContainerQueries 保证媒体查询与容器查询输出顺序正确;若主题启用了 modularCssLayers,还会包进 @layer sx

换言之,sx 的本质是把“key 字典 + 主题令牌 + 断点展开”这一整套风格函数管线串起来的一个统一入口。这也是文档强调“sx 是 CSS 超集、能自动清理未用断点”的实现基础。

BoxContainer 的组件实现

Box

Box.tsx 的实现非常精简:它通过 createBox({ defaultClassName, generateClassName }) 生成,BoxTypeMapdefaultComponent'div',因此默认渲染 <div>,同时它被类型化为 overridable component,可通过 component 属性渲染成任意元素或组件。BoxOwnProps 只声明了 childrenrefsx 三个自有 props,其余能力全部来自样式系统——可见它是“只负责承载 sx”的通用载体。

Box 组件自身有一套 Box.test.js 测试保障其行为,包括系统属性、responsive 值、component 覆盖等场景。

Container

Container.tsxcreateContainer() 生成,常用的受控 props 包括:

  • maxWidth:决定容器的最大宽度档位,默认 'lg',随屏幕尺寸增长;可传 false 禁用;
  • fixed:让 max-width 等于当前断点的 min-width,默认 false(默认是流式宽度,不会跳档);
  • disableGutters:移除左右内边距,默认 false

连同 GridStack 一起,这些通用布局组件与 Box 构成了 System 入口 导出的“布局组件层”。

何时该用 MUI System:取舍与性能

[usage.md](https://gitcode.com/GitHub_Trending/ma/material-ui/blob/95f68f3eb2fc42dcccf1ba2a0c4c956b0f22e452/docs/data/system/getting-started/usage/usage.md?utm_source=gitcode_repo_files) 对适用场景给出了清晰边界:

  • sx 最适用于“一次性样式”:为某个自定义组件快速打补丁、做微调;
  • styled-components API 更适合“需要支撑多种上下文”的组件:这类组件会在应用许多位置复用,接受不同的 props 组合、需要长期演化。

性能权衡

MUI System 依赖 CSS-in-JS,同时兼容 Emotion 与 styled-components 两种引擎。Usage 文档列举了它的利弊:

优点

  • 语法是 CSS 超集,附带(可选的)速记写法;
  • 系统会自动清理,只把页面实际用到的 CSS 发给客户端;初始包体成本固定,不会随你添加更多 CSS 属性而膨胀;
  • 核心成本来自 @emotion/react@mui/system,官方文档给出的总体量约 15 kB gzipped(该数值随版本演进会有变化,以你所安装版本实际体积为准);如果已经使用 Material UI 等核心库,则没有额外开销。

缺点

  • 运行时渲染性能有损耗。Usage 文档给出的归一化基准(该数据出自仓库内文档示例,可复现验证)如下:
Benchmark case Code snippet Time normalized
渲染 1,000 个原始元素 <div className="…"> 100ms
渲染 1,000 个普通组件 <Div> 112ms
渲染 1,000 个 styled 组件 <StyledDiv> 181ms
渲染 1,000 个 Box <Box sx={…}> 296ms

文档同时给出结论:对绝大多数场景足够快;当性能成为瓶颈时存在简单变通——例如渲染长列表时,用外层一个 Box 提供样式注入点,内部列表项使用纯 CSS 子选择器命中,把“每个列表项都跑一遍 sx”的代价摊薄掉。

安装与环境前提

根据 installation.md,MUI System 默认使用 Emotion 作为样式引擎,三条命令任选其一:

npm install @mui/system @emotion/react @emotion/styled
pnpm add @mui/system @emotion/react @emotion/styled
yarn add @mui/system @emotion/react @emotion/styled

注意 react 是 peer dependency,安装前应确保已就位,文档中声明的 peer 范围为 ^17.0.0 || ^18.0.0 || ^19.0.0

如果你更习惯 styled-components,则安装对应引擎替换包:

npm install @mui/system @mui/styled-engine-sc styled-components
pnpm add @mui/system @mui/styled-engine-sc styled-components
yarn add @mui/system @mui/styled-engine-sc styled-components

⚠️ 一个重要的适用前提(文档以 error 级提示强调):styled-components 不兼容服务端渲染(SSR)的 MUI 项目——babel-plugin-styled-components 无法处理 @mui 包内部的 styled() 工具。因此 SSR 项目被强烈建议使用 Emotion

进一步阅读

本文围绕官方 Overview 展开,更多细节可继续阅读仓库内以下配套文档与源码:

简言之,MUI System 通过 sx 把“CSS 编写”“主题令牌取值”“响应式断点”三件事合并进一个表达力极强的 prop 中,是快速落地自定义设计与统一视觉体系时最值得优先考虑的基础设施。

登录后查看全文
热门项目推荐
相关项目推荐