Material-UI 响应式设计 #
概述 #
MUI 提供了完整的响应式设计系统,包括断点、Grid 布局、隐藏组件等。
text
响应式系统
│
├── breakpoints 断点系统
├── Grid 响应式网格
├── Hidden 条件渲染
├── useMediaQuery 媒体查询
└── sx prop 响应式样式
断点系统 #
默认断点 #
jsx
const defaultBreakpoints = {
xs: 0, // 手机
sm: 600, // 平板竖屏
md: 900, // 平板横屏
lg: 1200, // 桌面
xl: 1536, // 大屏
};
自定义断点 #
jsx
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
breakpoints: {
values: {
mobile: 0,
tablet: 640,
laptop: 1024,
desktop: 1280,
},
},
});
断点范围 #
text
xs: 0px - 599px
sm: 600px - 899px
md: 900px - 1199px
lg: 1200px - 1535px
xl: 1536px+
useMediaQuery #
基础用法 #
jsx
import { useMediaQuery, useTheme } from '@mui/material';
function ResponsiveComponent() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const isTablet = useMediaQuery(theme.breakpoints.between('sm', 'md'));
const isDesktop = useMediaQuery(theme.breakpoints.up('md'));
return (
<div>
{isMobile && <MobileView />}
{isTablet && <TabletView />}
{isDesktop && <DesktopView />}
</div>
);
}
断点方法 #
jsx
const theme = useTheme();
theme.breakpoints.up('sm');
theme.breakpoints.down('sm');
theme.breakpoints.between('sm', 'md');
theme.breakpoints.only('sm');
theme.breakpoints.not('sm');
JavaScript 媒体查询 #
jsx
const matches = useMediaQuery('(min-width:600px)');
const isDark = useMediaQuery('(prefers-color-scheme: dark)');
const isPrint = useMediaQuery('print');
sx Prop 响应式 #
响应式属性值 #
jsx
<Box
sx={{
width: {
xs: '100%',
sm: '80%',
md: '60%',
lg: '50%',
},
p: {
xs: 1,
sm: 2,
md: 3,
},
fontSize: {
xs: '0.875rem',
md: '1rem',
},
}}
>
响应式 Box
</Box>
响应式样式对象 #
jsx
<Box
sx={{
display: {
xs: 'block',
md: 'flex',
},
flexDirection: {
xs: 'column',
md: 'row',
},
gap: {
xs: 1,
md: 2,
lg: 3,
},
}}
>
响应式布局
</Box>
条件样式 #
jsx
<Box
sx={{
bgcolor: {
xs: 'primary.light',
md: 'primary.main',
},
color: {
xs: 'primary.main',
md: 'primary.contrastText',
},
}}
>
响应式颜色
</Box>
Grid 响应式布局 #
基础响应式网格 #
jsx
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Box sx={{ p: 2, bgcolor: 'grey.200' }}>项目 1</Box>
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Box sx={{ p: 2, bgcolor: 'grey.200' }}>项目 2</Box>
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Box sx={{ p: 2, bgcolor: 'grey.200' }}>项目 3</Box>
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Box sx={{ p: 2, bgcolor: 'grey.200' }}>项目 4</Box>
</Grid>
</Grid>
响应式间距 #
jsx
<Grid container spacing={{ xs: 1, sm: 2, md: 3 }}>
{/* ... */}
</Grid>
响应式方向 #
jsx
<Grid
container
direction={{ xs: 'column', md: 'row' }}
spacing={2}
>
<Grid item xs={12} md={6}>
左侧
</Grid>
<Grid item xs={12} md={6}>
右侧
</Grid>
</Grid>
响应式对齐 #
jsx
<Grid
container
justifyContent={{ xs: 'center', md: 'flex-start' }}
alignItems={{ xs: 'center', md: 'flex-start' }}
>
{/* ... */}
</Grid>
Hidden 组件(已弃用) #
注意:Hidden 组件在 MUI v5 中已弃用,推荐使用 sx prop 或 useMediaQuery。
替代方案 #
jsx
// 旧方式
<Hidden smDown>
<div>仅在 sm 及以上显示</div>
</Hidden>
// 新方式 - 使用 sx prop
<Box sx={{ display: { xs: 'none', sm: 'block' } }}>
仅在 sm 及以上显示
</Box>
// 新方式 - 使用 useMediaQuery
function MyComponent() {
const theme = useTheme();
const isSmUp = useMediaQuery(theme.breakpoints.up('sm'));
if (!isSmUp) return null;
return <div>仅在 sm 及以上显示</div>;
}
响应式图片 #
基础响应式图片 #
jsx
<Box
component="img"
sx={{
width: {
xs: '100%',
md: '50%',
},
height: 'auto',
}}
src="/image.jpg"
alt="响应式图片"
/>
不同尺寸图片 #
jsx
<picture>
<source media="(min-width: 1200px)" srcSet="/large.jpg" />
<source media="(min-width: 600px)" srcSet="/medium.jpg" />
<img src="/small.jpg" alt="响应式图片" style={{ width: '100%' }} />
</picture>
响应式排版 #
响应式字体大小 #
jsx
<Typography
sx={{
fontSize: {
xs: '1.5rem',
sm: '2rem',
md: '2.5rem',
lg: '3rem',
},
}}
>
响应式标题
</Typography>
响应式字体 #
jsx
const theme = createTheme({
typography: {
h1: {
fontSize: '2.5rem',
'@media (min-width:600px)': {
fontSize: '3rem',
},
'@media (min-width:900px)': {
fontSize: '4rem',
},
},
},
});
使用 responsiveFontSizes #
jsx
import { createTheme, responsiveFontSizes } from '@mui/material/styles';
let theme = createTheme();
theme = responsiveFontSizes(theme);
响应式组件 #
响应式导航 #
jsx
function ResponsiveNav() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
return (
<AppBar position="static">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
Logo
</Typography>
{isMobile ? (
<IconButton color="inherit">
<MenuIcon />
</IconButton>
) : (
<Stack direction="row" spacing={2}>
<Button color="inherit">首页</Button>
<Button color="inherit">产品</Button>
<Button color="inherit">关于</Button>
</Stack>
)}
</Toolbar>
</AppBar>
);
}
响应式侧边栏 #
jsx
function ResponsiveSidebar() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [open, setOpen] = useState(false);
const drawer = (
<Box sx={{ width: 250 }}>
<List>
<ListItem button>
<ListItemText primary="首页" />
</ListItem>
<ListItem button>
<ListItemText primary="设置" />
</ListItem>
</List>
</Box>
);
return (
<Box sx={{ display: 'flex' }}>
{isMobile ? (
<Drawer open={open} onClose={() => setOpen(false)}>
{drawer}
</Drawer>
) : (
<Drawer variant="permanent">{drawer}</Drawer>
)}
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
{isMobile && (
<IconButton onClick={() => setOpen(true)}>
<MenuIcon />
</IconButton>
)}
<Typography>主内容</Typography>
</Box>
</Box>
);
}
响应式表格 #
jsx
function ResponsiveTable({ data }) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
if (isMobile) {
return (
<Stack spacing={2}>
{data.map((row) => (
<Card key={row.id}>
<CardContent>
<Typography variant="h6">{row.name}</Typography>
<Typography>价格: ¥{row.price}</Typography>
<Typography>数量: {row.quantity}</Typography>
</CardContent>
</Card>
))}
</Stack>
);
}
return (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>名称</TableCell>
<TableCell>价格</TableCell>
<TableCell>数量</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map((row) => (
<TableRow key={row.id}>
<TableCell>{row.name}</TableCell>
<TableCell>¥{row.price}</TableCell>
<TableCell>{row.quantity}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
实战示例:响应式布局 #
jsx
function ResponsiveLayout() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
return (
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
<AppBar position="static">
<Toolbar>
{isMobile && (
<IconButton edge="start" color="inherit" sx={{ mr: 2 }}>
<MenuIcon />
</IconButton>
)}
<Typography variant="h6" sx={{ flexGrow: 1 }}>
响应式应用
</Typography>
{!isMobile && (
<Stack direction="row" spacing={1}>
<Button color="inherit">首页</Button>
<Button color="inherit">产品</Button>
<Button color="inherit">关于</Button>
</Stack>
)}
</Toolbar>
</AppBar>
<Container maxWidth="lg" sx={{ mt: 4, mb: 4, flexGrow: 1 }}>
<Grid container spacing={3}>
<Grid item xs={12} md={8}>
<Paper sx={{ p: 2 }}>
<Typography variant="h4" gutterBottom>
主内容区
</Typography>
<Typography>
这是主内容区域,在移动端占满宽度,在桌面端占 2/3 宽度。
</Typography>
</Paper>
</Grid>
<Grid item xs={12} md={4}>
<Paper sx={{ p: 2 }}>
<Typography variant="h6" gutterBottom>
侧边栏
</Typography>
<Typography variant="body2">
这是侧边栏,在移动端占满宽度,在桌面端占 1/3 宽度。
</Typography>
</Paper>
</Grid>
</Grid>
</Container>
<Box
component="footer"
sx={{
py: 3,
px: 2,
mt: 'auto',
bgcolor: 'background.paper',
}}
>
<Container maxWidth="lg">
<Typography variant="body2" align="center">
© 2024 响应式应用
</Typography>
</Container>
</Box>
</Box>
);
}
下一步 #
继续学习 图标系统,了解 Material Icons 的使用方法!
最后更新:2026-03-28