Material-UI 教程
项目概述
Material-UI(现在称为 MUI)是一套基于 Material Design 的 React 组件库,提供了丰富的预构建组件,帮助开发者快速构建现代化、美观的 Web 应用。Material-UI 遵循 Google 的 Material Design 设计规范,提供了一致的视觉语言和交互体验,同时保持了高度的可定制性。
主要特点
- Material Design 实现:严格遵循 Google 的 Material Design 设计规范
- 丰富的组件库:提供了大量常用的 UI 组件
- 主题定制:支持深度定制主题,满足不同品牌需求
- 响应式设计:适配各种屏幕尺寸
- TypeScript 支持:完整的类型定义
- 性能优化:组件经过性能优化,确保流畅的用户体验
- 无障碍支持:符合 WCAG 标准,支持屏幕阅读器
- 生态系统丰富:提供了多个相关库,如 MUI X、MUI System 等
适用场景
- 构建现代化的 Web 应用
- 需要遵循 Material Design 规范的项目
- 快速原型开发
- 企业级应用
- 需要高度可定制 UI 的项目
安装与设置
方法一:使用 npm 或 yarn 安装
# 使用 npm
npm install @mui/material @emotion/react @emotion/styled
# 使用 yarn
yarn add @mui/material @emotion/react @emotion/styled方法二:使用 CDN 引入
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Material-UI 示例</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@mui/material@5.14.18/umd/material-ui.development.js"></script>
</head>
<body>
<div id="root"></div>
<script>
const { Button } = MaterialUI;
ReactDOM.render(
<Button variant="contained">Hello Material-UI</Button>,
document.getElementById('root')
);
</script>
</body>
</html>方法三:使用 Create React App
# 创建项目
npx create-react-app my-app
cd my-app
# 安装 Material-UI
npm install @mui/material @emotion/react @emotion/styled方法四:使用 Vite
# 创建项目
npm create vite@latest my-app -- --template react
cd my-app
# 安装 Material-UI
npm install @mui/material @emotion/react @emotion/styled核心概念
1. 组件使用
Material-UI 提供了丰富的组件,使用方式非常简单:
import React from 'react';
import { Button, TextField, Checkbox } from '@mui/material';
function App() {
return (
<div>
<Button variant="contained" color="primary">
Primary Button
</Button>
<TextField label="Basic text field" variant="outlined" />
<Checkbox label="Remember me" />
</div>
);
}
export default App;2. 主题系统
Material-UI 提供了强大的主题系统,支持定制颜色、排版、间距等:
import React from 'react';
import { createTheme, ThemeProvider, Button } from '@mui/material';
// 创建自定义主题
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
typography: {
fontFamily: 'Roboto, Arial, sans-serif',
},
});
function App() {
return (
<ThemeProvider theme={theme}>
<Button variant="contained" color="primary">
Themed Button
</Button>
<Button variant="contained" color="secondary">
Secondary Button
</Button>
</ThemeProvider>
);
}
export default App;3. 布局系统
Material-UI 提供了多种布局组件,如 Grid、Box、Stack 等:
import React from 'react';
import { Grid, Box, Stack, Paper } from '@mui/material';
function App() {
return (
<Box sx={{ flexGrow: 1, p: 3 }}>
{/* 使用 Grid 布局 */}
<Grid container spacing={3}>
<Grid item xs={12} sm={6} md={4}>
<Paper sx={{ p: 2 }}>Grid Item 1</Paper>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<Paper sx={{ p: 2 }}>Grid Item 2</Paper>
</Grid>
<Grid item xs={12} md={4}>
<Paper sx={{ p: 2 }}>Grid Item 3</Paper>
</Grid>
</Grid>
{/* 使用 Stack 布局 */}
<Stack direction="row" spacing={2} sx={{ mt: 3 }}>
<Paper sx={{ p: 2, flexGrow: 1 }}>Stack Item 1</Paper>
<Paper sx={{ p: 2, flexGrow: 1 }}>Stack Item 2</Paper>
<Paper sx={{ p: 2, flexGrow: 1 }}>Stack Item 3</Paper>
</Stack>
</Box>
);
}
export default App;4. 样式解决方案
Material-UI 提供了多种样式解决方案,包括:
使用 sx 属性
import React from 'react';
import { Box, Button } from '@mui/material';
function App() {
return (
<Box
sx={{
width: 300,
height: 200,
bgcolor: 'primary.main',
color: 'white',
p: 2,
borderRadius: 2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Button
sx={{
bgcolor: 'white',
color: 'primary.main',
'&:hover': {
bgcolor: 'grey.100',
},
}}
>
Styled Button
</Button>
</Box>
);
}
export default App;使用 styled 组件
import React from 'react';
import { styled, Button } from '@mui/material';
// 创建 styled 组件
const StyledBox = styled('div')({
width: 300,
height: 200,
backgroundColor: 'primary.main',
color: 'white',
padding: 16,
borderRadius: 8,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});
const StyledButton = styled(Button)({
backgroundColor: 'white',
color: 'primary.main',
'&:hover': {
backgroundColor: 'grey.100',
},
});
function App() {
return (
<StyledBox>
<StyledButton>Styled Button</StyledButton>
</StyledBox>
);
}
export default App;5. 状态管理
Material-UI 组件通常通过 props 和 React 的状态管理进行控制:
import React, { useState } from 'react';
import { TextField, Button, List, ListItem, ListItemText } from '@mui/material';
function App() {
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
const [newItem, setNewItem] = useState('');
const handleAddItem = () => {
if (newItem.trim()) {
setItems([...items, newItem]);
setNewItem('');
}
};
return (
<div>
<TextField
label="Add new item"
variant="outlined"
value={newItem}
onChange={(e) => setNewItem(e.target.value)}
style={{ marginRight: 8, width: 300 }}
/>
<Button variant="contained" color="primary" onClick={handleAddItem}>
Add Item
</Button>
<List style={{ marginTop: 16, width: 400 }}>
{items.map((item, index) => (
<ListItem key={index}>
<ListItemText primary={item} />
</ListItem>
))}
</List>
</div>
);
}
export default App;常用组件
1. Button
按钮组件用于触发操作:
import React from 'react';
import { Button, Stack } from '@mui/material';
function ButtonExample() {
return (
<Stack direction="row" spacing={2}>
<Button variant="text">Text Button</Button>
<Button variant="outlined">Outlined Button</Button>
<Button variant="contained">Contained Button</Button>
<Button variant="contained" color="secondary">
Secondary Button
</Button>
<Button variant="contained" disabled>
Disabled Button
</Button>
</Stack>
);
}
export default ButtonExample;2. TextField
文本输入框组件用于收集用户输入:
import React, { useState } from 'react';
import { TextField, Stack } from '@mui/material';
function TextFieldExample() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<Stack spacing={2} width={300}>
<TextField
label="Name"
variant="outlined"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<TextField
label="Email"
variant="outlined"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<TextField
label="Password"
variant="outlined"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</Stack>
);
}
export default TextFieldExample;3. Card
卡片组件用于展示相关信息的集合:
import React from 'react';
import { Card, CardContent, CardMedia, Typography, Button, CardActions } from '@mui/material';
function CardExample() {
return (
<Card sx={{ maxWidth: 345 }}>
<CardMedia
component="img"
height="140"
image="https://example.com/image.jpg"
alt="Card image"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
Card Title
</Typography>
<Typography variant="body2" color="text.secondary">
This is a sample card. Card content goes here. You can add any text or components inside the card.
</Typography>
</CardContent>
<CardActions>
<Button size="small">Share</Button>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
);
}
export default CardExample;4. Dialog
对话框组件用于显示重要信息或请求用户确认:
import React, { useState } from 'react';
import { Button, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, TextField } from '@mui/material';
function DialogExample() {
const [open, setOpen] = useState(false);
const [name, setName] = useState('');
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleSave = () => {
console.log('Saved name:', name);
setOpen(false);
};
return (
<div>
<Button variant="contained" onClick={handleClickOpen}>
Open Dialog
</Button>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Dialog Title</DialogTitle>
<DialogContent>
<DialogContentText>
Please enter your name:
</DialogContentText>
<TextField
autoFocus
margin="dense"
label="Name"
type="text"
fullWidth
variant="outlined"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleSave}>Save</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default DialogExample;5. AppBar
应用栏组件用于显示应用的标题、导航和操作:
import React from 'react';
import { AppBar, Toolbar, Typography, Button, IconButton, Menu, MenuItem } from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
function AppBarExample() {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleMenu = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2 }}
onClick={handleMenu}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
App Title
</Typography>
<Button color="inherit">Login</Button>
<Menu
id="menu-appbar"
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'left',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
open={open}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
</Toolbar>
</AppBar>
);
}
export default AppBarExample;6. Card
卡片组件用于展示相关信息的集合:
import React from 'react';
import { Card, CardContent, CardMedia, Typography, Button, CardActions } from '@mui/material';
function CardExample() {
return (
<Card sx={{ maxWidth: 345 }}>
<CardMedia
component="img"
height="140"
image="https://example.com/image.jpg"
alt="Card image"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
Card Title
</Typography>
<Typography variant="body2" color="text.secondary">
This is a sample card. Card content goes here. You can add any text or components inside the card.
</Typography>
</CardContent>
<CardActions>
<Button size="small">Share</Button>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
);
}
export default CardExample;7. Tabs
标签页组件用于在同一页面中切换不同的内容:
import React, { useState } from 'react';
import { Tabs, Tab, Box, Typography } from '@mui/material';
function TabsExample() {
const [value, setValue] = useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Box sx={{ width: '100%', maxWidth: 500 }}>
<Tabs
value={value}
onChange={handleChange}
variant="fullWidth"
centered
>
<Tab label="Tab 1" />
<Tab label="Tab 2" />
<Tab label="Tab 3" />
</Tabs>
<Box sx={{ p: 3 }}>
{value === 0 && <Typography>Content of Tab 1</Typography>}
{value === 1 && <Typography>Content of Tab 2</Typography>}
{value === 2 && <Typography>Content of Tab 3</Typography>}
</Box>
</Box>
);
}
export default TabsExample;8. Select
选择器组件用于从选项中选择值:
import React, { useState } from 'react';
import { FormControl, InputLabel, Select, MenuItem, Box } from '@mui/material';
function SelectExample() {
const [age, setAge] = useState('');
const handleChange = (event) => {
setAge(event.target.value);
};
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="age-select-label">Age</InputLabel>
<Select
labelId="age-select-label"
id="age-select"
value={age}
label="Age"
onChange={handleChange}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</Box>
);
}
export default SelectExample;高级功能
1. 自定义主题
Material-UI 支持深度定制主题,包括颜色、排版、间距等:
import React from 'react';
import { createTheme, ThemeProvider, Button, Typography, Box } from '@mui/material';
// 创建自定义主题
const theme = createTheme({
palette: {
primary: {
main: '#2196f3',
light: '#64b5f6',
dark: '#1976d2',
contrastText: '#fff',
},
secondary: {
main: '#f50057',
light: '#ff4081',
dark: '#c51162',
contrastText: '#fff',
},
background: {
default: '#f5f5f5',
paper: '#fff',
},
},
typography: {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
h1: {
fontSize: '2.5rem',
fontWeight: 500,
},
h2: {
fontSize: '2rem',
fontWeight: 500,
},
},
spacing: 8,
shape: {
borderRadius: 8,
},
});
function CustomThemeExample() {
return (
<ThemeProvider theme={theme}>
<Box sx={{ p: 3, bgcolor: 'background.default', minHeight: 300 }}>
<Typography variant="h1" component="h2" gutterBottom>
Custom Theme Example
</Typography>
<Button variant="contained" color="primary" sx={{ mr: 2 }}>
Primary Button
</Button>
<Button variant="contained" color="secondary">
Secondary Button
</Button>
</Box>
</ThemeProvider>
);
}
export default CustomThemeExample;2. 样式系统
Material-UI 提供了强大的样式系统,包括 sx 属性、styled 组件和系统实用程序:
import React from 'react';
import { styled, Box, Button } from '@mui/material';
// 使用 styled 组件
const StyledContainer = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: theme.spacing(4),
backgroundColor: theme.palette.grey[100],
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shadows[2],
[theme.breakpoints.up('md')]: {
flexDirection: 'row',
gap: theme.spacing(2),
},
}));
// 使用 sx 属性
function StyleSystemExample() {
return (
<StyledContainer>
<Button
variant="contained"
sx={{
bgcolor: 'primary.main',
'&:hover': {
bgcolor: 'primary.dark',
},
px: 4,
py: 1.5,
borderRadius: 2,
}}
>
Styled Button 1
</Button>
<Button
variant="outlined"
sx={{
borderColor: 'secondary.main',
color: 'secondary.main',
'&:hover': {
borderColor: 'secondary.dark',
bgcolor: 'secondary.light',
},
px: 4,
py: 1.5,
borderRadius: 2,
}}
>
Styled Button 2
</Button>
</StyledContainer>
);
}
export default StyleSystemExample;3. 性能优化
对于大型应用,可以使用一些技巧来优化 Material-UI 组件的性能:
使用 React.memo
import React, { memo } from 'react';
import { List, ListItem, ListItemText } from '@mui/material';
const DataList = memo(({ items }) => {
return (
<List>
{items.map((item, index) => (
<ListItem key={index}>
<ListItemText primary={item} />
</ListItem>
))}
</List>
);
});
function App() {
const [items] = React.useState(['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5']);
const [count, setCount] = React.useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Increment: {count}
</button>
<DataList items={items} />
</div>
);
}
export default App;使用 useMemo 和 useCallback
import React, { useMemo, useCallback, useState } from 'react';
import { TextField, Button, List, ListItem, ListItemText } from '@mui/material';
function App() {
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
const [newItem, setNewItem] = useState('');
const [filter, setFilter] = useState('');
// 使用 useMemo 缓存过滤后的列表
const filteredItems = useMemo(() => {
if (!filter) return items;
return items.filter(item => item.toLowerCase().includes(filter.toLowerCase()));
}, [items, filter]);
// 使用 useCallback 缓存事件处理函数
const handleAddItem = useCallback(() => {
if (newItem.trim()) {
setItems(prevItems => [...prevItems, newItem]);
setNewItem('');
}
}, [newItem]);
return (
<div>
<TextField
label="Filter"
variant="outlined"
value={filter}
onChange={(e) => setFilter(e.target.value)}
style={{ marginBottom: 16, width: 300 }}
/>
<TextField
label="Add new item"
variant="outlined"
value={newItem}
onChange={(e) => setNewItem(e.target.value)}
style={{ marginRight: 8, width: 300 }}
/>
<Button variant="contained" color="primary" onClick={handleAddItem}>
Add Item
</Button>
<List style={{ marginTop: 16, width: 400 }}>
{filteredItems.map((item, index) => (
<ListItem key={index}>
<ListItemText primary={item} />
</ListItem>
))}
</List>
</div>
);
}
export default App;4. 无障碍支持
Material-UI 提供了内置的无障碍支持,同时也提供了一些工具来帮助开发者创建更无障碍的应用:
import React from 'react';
import { Button, TextField, FormControlLabel, Checkbox, Box } from '@mui/material';
import { useId } from '@mui/material/utils';
function AccessibilityExample() {
// 使用 useId 创建唯一的 ID,确保无障碍性
const nameId = useId();
const emailId = useId();
const termsId = useId();
return (
<Box sx={{ p: 3, maxWidth: 400 }}>
<TextField
id={nameId}
label="Name"
variant="outlined"
fullWidth
margin="normal"
aria-required="true"
/>
<TextField
id={emailId}
label="Email"
variant="outlined"
type="email"
fullWidth
margin="normal"
aria-required="true"
/>
<FormControlLabel
control={<Checkbox id={termsId} />}
label="I agree to the terms and conditions"
/>
<Button
variant="contained"
color="primary"
fullWidth
sx={{ mt: 2 }}
>
Submit
</Button>
</Box>
);
}
export default AccessibilityExample;最佳实践
1. 组件组织
- 按功能模块组织组件
- 使用文件夹结构管理相关组件
- 为组件添加清晰的文档和注释
2. 样式管理
- 使用 Material-UI 的样式系统(sx 属性、styled 组件)
- 避免直接覆盖组件样式
- 对于全局样式,使用 createTheme 进行配置
3. 性能优化
- 使用 React.memo 缓存组件
- 合理使用 useMemo 和 useCallback
- 避免在 render 函数中创建不必要的对象
- 对于长列表,使用虚拟滚动
4. 可访问性
- 使用语义化的 HTML 元素
- 确保所有交互元素都有适当的 ARIA 属性
- 测试组件在屏幕阅读器中的表现
- 确保键盘导航正常工作
5. 主题管理
- 为整个应用使用单一的主题提供者
- 集中管理主题配置
- 对于需要不同主题的部分,使用嵌套的 ThemeProvider
6. 测试
- 为组件编写单元测试
- 测试组件在不同主题下的表现
- 测试组件的响应式行为
- 测试表单验证逻辑
常见问题与解决方案
1. 样式冲突
问题:Material-UI 样式与项目其他样式冲突。
解决方案:
- 使用 CSS Modules 或 styled-components
- 为 Material-UI 组件添加前缀
- 使用 createTheme 的 components 属性覆盖组件样式
2. 组件渲染性能问题
问题:大型表单或表格渲染缓慢。
解决方案:
- 使用虚拟滚动
- 优化组件渲染,避免不必要的重新渲染
- 使用 React.lazy 和 Suspense 懒加载组件
3. 主题定制问题
问题:主题定制不生效。
解决方案:
- 确保正确配置 createTheme
- 检查 ThemeProvider 的嵌套顺序
- 验证组件是否支持主题定制
4. 响应式设计问题
问题:组件在某些屏幕尺寸下显示异常。
解决方案:
- 使用 Material-UI 的断点系统
- 为不同屏幕尺寸设计不同的布局
- 测试组件在各种屏幕尺寸下的表现
5. 国际化配置问题
问题:国际化配置不生效。
解决方案:
- 使用 Material-UI 的 LocalizationProvider
- 确保正确导入语言包
- 验证组件是否支持国际化
参考资源
总结
Material-UI 是一套功能强大、设计精美的 React 组件库,基于 Google 的 Material Design 设计规范,提供了丰富的预构建组件,帮助开发者快速构建现代化的 Web 应用。通过本教程的学习,你应该已经掌握了 Material-UI 的核心概念、使用方法和最佳实践。
Material-UI 的生态系统非常丰富,包括 MUI Core(核心组件库)、MUI X(高级组件,如数据网格、日期选择器等)、MUI System(样式系统)、MUI Base(无样式组件)等。这些工具可以帮助你更高效地构建各种类型的应用。
作为最流行的 React UI 组件库之一,Material-UI 拥有庞大的开发者社区和完善的文档,这使得它成为构建现代化 Web 应用的理想选择。无论是简单的原型还是复杂的企业级应用,Material-UI 都能满足你的需求。
随着 Material-UI 的不断发展,它也在持续改进和更新,添加新的组件和功能,以适应现代 Web 开发的需求。建议你持续关注 Material-UI 的官方文档和社区动态,以便及时了解最新的功能和最佳实践。