如何用 Material UI 的 Masonry 组件搭建响应式瀑布流布局(含 SSR 场景)?
【免费下载链接】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 提供的Masonry组件可以直接完成这件事。它在@mui/lab包中,把任意子元素(包括<div />、<img />)排成宽度相同、高度可变的列块,列与列之间的间距可配置。本文按官方文档docs/data/material/components/masonry/masonry.md及其示例代码,给出从基础布局到响应式列数/间距、再到服务端渲染(SSR)场景的完整搭建过程。
先了解 Masonry 的排列规则
官方文档对 Masonry 的布局规则描述如下:
- Masonry 维护一组宽度一致、高度各异的内容块,内容按行排布;
- 当某一行的列数达到
columns指定的数量后,下一个元素会进入新的一行,并加入当前最短的列,以优化空间利用; Masonry是一个容器,可以接收任意元素作为子项。
这条“总是加入最短列”的规则是后面验证布局是否符合预期的依据:如果某个元素没有落到当前最短的列,说明组件没有正确工作或被外部样式干扰了。
准备依赖
官方示例代码中的导入方式是:
import Masonry from '@mui/lab/Masonry'; import Box from '@mui/material/Box'; import Paper from '@mui/material/Paper'; import { styled } from '@mui/material/styles';即项目需要依赖@mui/lab(Masonry 所在包)与@mui/material(Box、Paper 等示例中使用的基础组件)。请确认你的package.json中已包含这两个包后再开始下面的步骤。
搭建基础瀑布流
对应官方示例 BasicMasonry.tsx:
import Box from '@mui/material/Box'; import { styled } from '@mui/material/styles'; import Paper from '@mui/material/Paper'; import Masonry from '@mui/lab/Masonry'; const heights = [150, 30, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 30, 50, 80]; const Item = styled(Paper)(({ theme }) => ({ backgroundColor: '#fff', ...theme.typography.body2, padding: theme.spacing(0.5), textAlign: 'center', color: (theme.vars || theme).palette.text.secondary, ...theme.applyStyles('dark', { backgroundColor: '#1A2027', }), })); export default function BasicMasonry() { return ( <Box sx={{ width: 500, minHeight: 393 }}> <Masonry columns={4} spacing={2}> {heights.map((height, index) => ( <Item key={index} sx={{ height }}> {index + 1} </Item> ))} </Masonry> </Box> ); }关键配置:
columns={4}:固定为 4 列;spacing={2}:元素之间的间距。注意文档明确指出,传给spacing的值会乘以主题中的 spacing 字段(即theme.spacing(2)),而不是 2 像素;- 子项通过
sx={{ height }}给定不同高度,模拟真实业务中高度不一的卡片。
验证方式:渲染后每一行应恰好容纳 4 个等宽项,项高各不同;第 5 个及之后的项应出现在当前最短的列的下方。heights数组中的具体数值是文档示例数据,你可以替换为自己的内容,验证时以“列宽一致 + 新项加入最短列”为准。
让列数和间距支持响应式
这是标题中“响应式”的核心部分。文档确认columns和spacing都接受以断点为键的响应式对象。
对应 ResponsiveColumns.tsx:
<Masonry columns={{ xs: 3, sm: 4 }} spacing={2}> {heights.map((height, index) => ( <Item key={index} sx={{ height }}> {index + 1} </Item> ))} </Masonry>在小屏幕(xs断点)下显示 3 列,sm断点及以上显示 4 列,其余代码与基础示例相同。
对应 ResponsiveSpacing.tsx:
<Masonry columns={3} spacing={{ xs: 1, sm: 2, md: 3 }}> {heights.map((height, index) => ( <Item key={index} sx={{ height }}> {index + 1} </Item> ))} </Masonry>列数固定为 3,但间距随断点变化:xs下为theme.spacing(1),sm下为theme.spacing(2),md下为theme.spacing(3)。
验证方式:拖动浏览器窗口跨越sm、md断点,观察列数或间距是否按上面配置切换。官方文档中这两个示例均带有可交互演示(见 masonry.md 的 Columns 与 Spacing 两节)。
可选:改为从左到右的顺序排布
默认情况下 Masonry 始终把新元素加入最短列。如果业务上要求元素严格按书写顺序从左到右排布,可以给Masonry增加sequential属性。官方文档说明:启用sequential后,“items are added in order from left to right rather than adding to the shortest column”。
对应 Sequential.tsx:
<Masonry columns={4} spacing={2} defaultHeight={450} defaultColumns={4} defaultSpacing={1} sequential > {heights.map((height, index) => ( <Item key={index} sx={{ height }}> {index + 1} </Item> ))} </Masonry>注意此示例同时设置了defaultHeight、defaultColumns、defaultSpacing(SSR 相关,下一节解释),如果不需要 SSR 支持,只保留columns、spacing、sequential即可。
SSR 场景:defaultHeight / defaultColumns / defaultSpacing
在水滴流这种依赖实际渲染高度计算位置的布局里,服务端渲染阶段拿不到元素真实高度。官方文档的 “Server-side rendering” 一节给出了对策:使用defaultHeight、defaultColumns和defaultSpacing这三个属性来支持 SSR。
对应 SSRMasonry.tsx:
<Box sx={{ width: 500, minHeight: 393 }}> <Masonry columns={4} spacing={2} defaultHeight={450} defaultColumns={4} defaultSpacing={1} > {heights.map((height, index) => ( <Item key={index} sx={{ height }}> {index + 1} </Item> ))} </Masonry> </Box>三个属性各自的作用,结合文档与源码 Masonry.js 可以明确:
defaultColumns:SSR 阶段按此列数把容器分成等宽列(源码中据此计算每列宽度和行分组),同时它是触发 SSR 模式的判断条件之一;defaultHeight:SSR 阶段容器的占位高度。文档明确要求:defaultHeight应大到足以渲染所有行;defaultSpacing:SSR 阶段参与列间距计算的间距值。源码中与spacing一样会先经过theme.spacing换算(parseToNumber(theme.spacing(ownerState.defaultSpacing)))。
源码中的判断逻辑是:只有当defaultHeight、defaultColumns、defaultSpacing三者都提供时,组件才按 SSR 样式渲染(见 Masonry.js 中isSSR的判定);只设置其中一两个不会启用 SSR 样式。
SSR 场景的验证与限制(均来自官方文档):
- 服务端渲染的 HTML 中,子项按
defaultColumns列均分,容器高度为defaultHeight; - 文档明确说明:在服务端渲染的情况下,元素不会被加入最短列——这是 SSR 结果与客户端布局的主要差异,首屏可能出现“客户端水合后布局再次调整”的现象,属于预期行为;
- 若
defaultHeight给得不够大,不足以容纳所有行,属于配置错误,需要调大该值。
defaultHeight={450}等具体数值是文档示例的取值,实际项目应根据自己的行数与列数重新估算,保证“足够渲染所有行”。
参考的官方示例与文档位置
| 内容 | 路径 |
|---|---|
| Masonry 文档(含全部示例入口) | docs/data/material/components/masonry/masonry.md |
| 基础瀑布流 | docs/data/material/components/masonry/BasicMasonry.tsx |
| 响应式列数 | docs/data/material/components/masonry/ResponsiveColumns.tsx |
| 响应式间距 | docs/data/material/components/masonry/ResponsiveSpacing.tsx |
| 变高子项(如 Accordion) | docs/data/material/components/masonry/MasonryWithVariableHeightItems.tsx |
| 顺序排布 | docs/data/material/components/masonry/Sequential.tsx |
| SSR 示例 | docs/data/material/components/masonry/SSRMasonry.tsx |
| 组件源码 | packages/mui-lab/src/Masonry/Masonry.js |
两点边界补充:
- 文档中的 Image masonry 示例展示了用 Masonry 排布图片,其中图片按行排序;如果你的场景要求图片按列排序,文档建议改用 ImageList 组件的 masonry 模式(参见 docs/data/material/components/image-list/image-list.md)。
- 变高内容的另一种形态:子项不是固定像素高度而是可展开/折叠的组件(如 Accordion)时,可直接作为 Masonry 子项,Masonry 会依据实际高度在列间移动它们(见 MasonryWithVariableHeightItems.tsx),无需额外配置。
【免费下载链接】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),仅供参考