news 2026/9/7 9:25:20

Material UI 容器查询实战:theme.containerQueries 与 sx “@” 简写语法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Material UI 容器查询实战:theme.containerQueries 与 sx “@” 简写语法

Material UI 容器查询实战:theme.containerQueries 与 sx “@” 简写语法

【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Google's Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui

本文基于 Material UI 官方文档 container-queries.md,系统讲解theme.containerQueries的完整 API、sx属性中@<size>/@<size>/<name>简写语法的规则与陷阱,并结合 packages/mui-system 源码揭示其底层实现机制。读完本文,你可以直接在组件库中写出“根据父容器宽度而非视口宽度”进行响应的样式,并理解 Media Query 方法如何被复用到 Container Query 场景。

为什么需要容器查询

传统的theme.breakpoints生成的@media规则判断的是浏览器视口宽度;而 CSS Container Query 判断的是元素所在容器的宽度。这使得同一组件放在侧边栏、主内容区或弹层中时能各自独立地调整布局——这正是 BasicContainerQueries.tsx 演示的“卡片在 350px / 500px 容器断点处切换横排/竖排”的场景。

Material UI 的用法约定很直接:使用theme.containerQueries加上theme.breakpoints的任意方法即可。传入的取值支持三种形式:

  • 无单位数字:按像素渲染(350等价于350px);
  • 字符串:如'500px''40rem'
  • 断点键:如'sm''md'(复用主题断点的像素值)。
theme.containerQueries.up('sm'); // => '@container (min-width: 600px)'

前置条件:必须有一个祖先元素声明了容器类型(如containerType: 'inline-size'),否则容器查询不生效。官方演示中是在外层Box上通过sx写入containerType: 'inline-size'来启用该能力。

完整的 API:与 breakpoints 一一对应

容器查询支持断点 API 的全部方法,且断点取值直接继承主题的breakpoints.values(默认为xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536,单位px):

调用输出(默认断点)
theme.containerQueries.up('sm')@container (min-width: 600px)
theme.containerQueries.down('md')@container (max-width: 899.95px)
theme.containerQueries.only('md')@container (min-width: 600px) and (max-width: 1199.95px)
theme.containerQueries.between('sm', 'lg')@container (min-width: 600px) and (max-width: 1199.95px)
theme.containerQueries.not('sm')@container (max-width: 600px)(见下文范围语法说明)

这里有一个值得注意的细节:down/between/not生成的max-width会减去step / 100(默认step: 5,即减0.05)。这个机制源自 createBreakpoints.ts 中down的实现(value) - step / 100,目的是让相邻断点互斥(up('sm')down('sm')不重叠)。单元测试 cssContainerQueries.test.ts 明确断言了down('sm')等于@container (max-width:599.95px),所以文档示例中书写的900px只是约数,实际渲染值是899.95px

命名容器(Named containment contexts)

若 DOM 中存在命名容器,可以在调用containerQueries时传入容器名称,访问同一套断点方法:

theme.containerQueries('sidebar').up('500px'); // => '@container sidebar (min-width: 500px)'

从源码结构看,实现位于 cssContainerQueries.ts:containerQueries本身既是对象(直接调用up/down/...),又是一个函数——传入name后返回一个“附着”了同名方法的节点。核心的toContainerQuery只做字符串替换:把断点方法生成的@media ...替换为@container ...@container <name> ...,因此它能天然继承 breakpoints 的全部取值逻辑,包括断点键解析、step偏移等。

实战示例一:styled 方式配合主题断点

以下代码完整来自 BasicContainerQueries.tsx,展示了一张房产卡片随容器宽度变化的布局:容器小于 350px 时图片在上、内容在下(flexDirection: column);达到 350px 时切换为横排;达到 500px 时图片宽度锁定为 240px、内容区内边距加大。

import { styled } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Card from '@mui/material/Card'; import CardContent from '@mui/material/CardContent'; const DynamicCard = styled(Card)(({ theme }) => ({ display: 'flex', flexDirection: 'column', [theme.containerQueries.up(350)]: { flexDirection: 'row', }, })); const Image = styled('img')(({ theme }) => ({ alignSelf: 'stretch', aspectRatio: '16 / 9', objectFit: 'cover', width: '100%', maxHeight: 160, transition: '0.4s', [theme.containerQueries.up(350)]: { maxWidth: '36%', maxHeight: 'initial', }, [theme.containerQueries.up(500)]: { maxWidth: 240, }, })); const Content = styled(CardContent)(({ theme }) => ({ display: 'flex', flexDirection: 'column', gap: theme.spacing(1), padding: theme.spacing(2), flex: 'auto', transition: 'padding 0.4s', [theme.containerQueries.up(500)]: { padding: theme.spacing(3), }, })); export default function BasicContainerQueries() { return ( <Box sx={{ overflow: 'auto', resize: 'horizontal', // 允许用户拖动改变容器宽度 width: 400, maxWidth: 'min(80vw, 600px)', containerType: 'inline-size', // required for container queries }} > <DynamicCard variant="outlined"> <Image alt="The house from the offer." src="..." /> <Content>{/* ... 卡片内容 ... */}</Content> </DynamicCard> </Box> ); }

演示页面配套的 ResizableDemo.js 会在 0px、350px、500px 三个位置画出虚线参考线,方便读者拖动容器时直观看到两个断点的触发位置。

实战示例二:sx 属性的@简写语法

sx中使用时,不必显式引用主题:直接在样式值对象里用@<size>@<size>/<name>作为键即可生成容器查询,无需引用主题。

  • <size>:一个宽度值(数字或带单位字符串)或断点键;
  • <name>(可选):命名容器上下文。

以下代码取自 SxPropContainerQueries.tsx,与上一节的 styled 版本等价,但完全内联在sx中:

<Card variant="outlined" sx={{ display: 'flex', flexDirection: { '@': 'column', // 容器任意宽度时(等价 @container 0px 起点) '@350': 'row', // 容器 >= 350px 时 }, }} > <Box component="img" sx={{ maxHeight: { '@': 160, '@350': 'initial' }, maxWidth: { '@350': '36%', '@500': 240 }, }} /> <CardContent sx={{ padding: { '@': 2, '@500': 3 }, // 简写值 2/3 走 spacing 换算 }} /> </Card>

简写语法的三个关键规则(Caveats)

  1. 无单位数值按px渲染@500等价于500px;但@500px错误写法,不会被正确渲染——@前缀场景下不要再带px单位。
  2. @渲染为0px'@': value表示“容器查询始终命中”的基准样式(min-width: 0px)。
  3. 同一组容器查询必须使用相同单位,且排序按数值大小而非单位换算:
// ✅ 单位一致,能按 0 < 20 < 40 正确升序排列 padding: { '@40em': 4, '@20em': 2, '@': 0, } // ❌ 40em 与 50px 单位不一致,排序会出错 // (40em 通常大于 50px,但按数值 40 < 50 排序) padding: { '@40em': 4, '@50': 2, '@': 0, }

第 3 条规则直接对应源码:cssContainerQueries.ts 中的sortContainerQueries用正则min-width:\s*([0-9.]+)提取min-width数值部分做纯数字排序,源码注释也明确警告 “this function does not work and will not support multiple units”(不支持混合单位)。该函数由 styleFunctionSx.js 引入,在sx处理流程中对以@container开头的键重新排序,保证输出的 CSS 按宽度从低到高排列、后写覆盖前写。

源码实现解析

1. 主题的注入点。在 createTheme.js 中,createTheme流程末尾执行muiTheme = cssContainerQueries(muiTheme),因此每个通过createTheme创建的主题都自动获得containerQueries,无需手动配置;其类型声明则放在createTheme.d.ts中合并进主题对象。

2. 断点方法的容器化包装。cssContainerQueries函数为up/down/between/only/not五个方法各生成一个包装版本,统一调用toContainerQuery

const toContainerQuery = (mediaQuery: string, name?: string) => mediaQuery.replace('@media', name ? `@container ${name}` : '@container');

3.not()的特殊处理。@media not all and (...)的写法在@container中不合法,源码对not单独做了逻辑反转:检测到not all and时,把min-width:替换为width<max-width:替换为width>and替换为or,生成 CSS 容器查询范围语法。因此文档中not('sm')对中间断点的语义是“容器宽度小于 600px 或大于 900px”。

4.@简写的识别与解析。sx 的响应式键处理(breakpoints.ts)中,先经isCqShorthand判断键是否为容器查询简写('@'@<断点键>或匹配/^@\d/的数字开头),再交给getContainerQuery用正则/^@([^/]+)?\/?(.+)?$/拆出<size><name>,最终调用theme.containerQueries(name).up(value)。注意两点:

  • 简写语法固定走up(),即@350生成的是min-width查询;
  • 无效简写在开发环境(非 production)会抛出带格式提示的错误,说明合法格式为@<breakpoint | number>@<breakpoint | number>/<container>,例如@sm@600@40rem/sidebar

5. 测试佐证。cssContainerQueries.test.ts 覆盖了:各方法在默认断点下的精确输出(如only('sm')@container (min-width:600px) and (max-width:899.95px))、命名容器(containerQueries('sidebar').up('sm')@container sidebar (min-width:600px)),以及isCqShorthand@@xs@200@15.5rem判真,对@media (min-width:600px)@page判假的边界用例,可视为该功能的验收基准。

小结

Material UI 的容器查询方案可概括为三点:

  1. theme.containerQueries复用断点 API 的全部方法(up/down/between/only/not),取值可为断点键、无单位数字或带单位字符串,支持命名容器上下文;
  2. sx中可用@<size>/@<size>/<name>简写免主题引用,但注意“无单位即 px”“裸@即 0px”“同一组查询单位必须一致”三条规则;
  3. 使用前提是 DOM 中存在声明了containerType(如inline-size)的祖先元素,且容器查询本身依赖浏览器的 CSS 容器查询支持。

若需要回顾断点方法本身的取值语义,可继续阅读 breakpoints 文档 及其实现 createBreakpoints.ts。

【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Google's Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 9:24:22

拼多多客服机器人开发实战:API接入与协议模拟全攻略

简介&#xff1a;基于拼多多官方平台接入的智能客服机器人方案&#xff0c;面向拼多多商家、客服主管及店铺运营人员&#xff0c;解决促销高峰或日常咨询量大时人工响应不及时、客服成本高等问题。方案以官方平台插件为基础&#xff0c;具备自动回复、语义理解、情感分析等能力…

作者头像 李华
网站建设 2026/9/7 9:24:18

CAD转SHP带属性插件:解决DWG转GIS数据丢失与乱码难题

简介&#xff1a;这款CAD转SHP带属性转换插件&#xff0c;主要面向测绘、GIS、规划、自然资源管理等行业的工程技术人员。日常工作中常需把CAD图形转为Shapefile&#xff0c;或反向加载&#xff0c;属性信息容易丢失&#xff1b;该插件只需基于AutoCAD 2008环境&#xff0c;无需…

作者头像 李华
网站建设 2026/9/7 9:23:03

PageOffice Java版部署指南:控件安装与环境配置常见坑解析

简介&#xff1a;PageOffice 4.6.0.4 的 Java 版离线资源包&#xff0c;面向需要在 Web 项目中集成 Office 文档在线编辑、预览与协同处理的 Java 开发人员&#xff0c;可用于 OA、ERP、政务系统等常见业务场景。压缩包包含完整示例工程&#xff0c;文件总数达 1030 个&#xf…

作者头像 李华
网站建设 2026/9/7 9:17:39

毕业论文降重与润色:从传统方法到智能工具的进阶之路

1. 引言&#xff1a;论文修改的十字路口 毕业论文提交前夕&#xff0c;几乎每一位毕业生都会面临同一个难题&#xff1a;如何在不改变学术原意的前提下&#xff0c;让论文表达更精炼、结构更清晰、查重结果更理想&#xff1f;面对琳琅满目的修改方式&#xff0c;我和身边的同学…

作者头像 李华