目录

Material UI:Google Material Design React 组件库完全指南

学习目标

通过本文,你将全面掌握以下核心能力:

  • 深入理解 Material UI 的项目定位、设计理念和技术架构
  • 掌握 Material UI 的核心组件体系和高级特性
  • 学会在 React 项目中安装、配置和定制 Material UI
  • 掌握主题定制、CSS-in-JS 和组件变体的高级用法
  • 理解从旧版本(v4/v5)升级到最新版本的最佳实践
  • 学会参与 Material UI 开源贡献的流程

1. 项目概述

1.1 是什么

Material UI 是一个全面的 React 组件库,独立实现了 Google 的 Material Design 设计系统。它被一些世界顶级产品团队信任,因为在超过十年的开发过程中,经过了数千名开源贡献者的严格实战检验。

1.2 核心数据

指标数值
GitHub Stars98k
GitHub Forks32.7k
贡献者3,091
被使用数200万+ 仓库
发布版本562
最新稳定版v7.3.9
最新预发布v9.0.0-beta.1
LicenseMIT

1.3 技术栈

语言占比
JavaScript61.1%
TypeScript38.6%
其他0.3%

1.4 MUI 生态

Material UI 是 MUI 生态的核心产品,MUI 生态还包括:

产品说明
MUI X高级复杂组件(Data Grid、Date Pickers、Charts 等)
MUI Base无样式的底层组件
MUI System低级样式工具
MUI Joy新设计语言的实验组件
MUI Templates高级模板和主题

2. 核心特性详解

2.1 组件体系

Material UI 提供了丰富的组件,涵盖几乎所有 UI 场景:

类别组件示例
布局Box、Container、Grid、Stack、Divider
导航AppBar、Drawer、Menu、Tabs、Breadcrumbs
输入Button、TextField、Select、Checkbox、Radio、Switch
反馈Alert、Snackbar、Dialog、Progress、Chip
数据展示Table、Card、List、Avatar、Tooltip
编排Modal、Popover、Popper、Backdrop

2.2 TypeScript 支持

Material UI 从 v5 开始完全使用 TypeScript 开发,提供完善的类型提示:

import { Button, ButtonProps } from '@mui/material';

// 带有完整类型安全的组件
function CustomButton(props: ButtonProps & { variant?: 'contained' | 'outlined' }) {
  return <Button {...props} />;
}

2.3 CSS-in-JS 方案

Material UI 使用 Emotion 作为默认的 CSS-in-JS 解决方案:

import { styled } from '@mui/material/styles';

// 创建自定义样式组件
const StyledButton = styled(Button)(({ theme }) => ({
  backgroundColor: theme.palette.primary.main,
  '&:hover': {
    backgroundColor: theme.palette.primary.dark,
  },
}));

2.4 主题系统

Material UI 提供强大的主题定制能力:

import { createTheme, ThemeProvider } from '@mui/material/styles';

const theme = createTheme({
  palette: {
    primary: {
      main: '#1976d2',
    },
    secondary: {
      main: '#dc004e',
    },
  },
  typography: {
    fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
  },
  spacing: 8, // 基线网格
});

export default function App() {
  return (
    <ThemeProvider theme={theme}>
      <Button>Hello Material UI</Button>
    </ThemeProvider>
  );
}

2.5 组件变体(Variants)

支持多种预定义变体:

// Button 变体
<Button variant="text">Text</Button>
<Button variant="contained">Contained</Button>
<Button variant="outlined">Outlined</Button>

// TextField 变体
<TextField variant="outlined" />
<TextField variant="filled" />
<TextField variant="standard" />

2.6 RTL 和国际化

原生支持从右到左(RTL)布局:

const theme = createTheme({
  direction: 'rtl', // 启用 RTL
});

3. 安装与快速上手

3.1 安装

# 使用 npm
npm install @mui/material @emotion/react @emotion/styled

# 使用 yarn
yarn add @mui/material @emotion/react @emotion/styled

# 使用 pnpm
pnpm add @mui/material @emotion/react @emotion/styled

3.2 字体设置

Material UI 依赖 Roboto 字体:

<link
  rel="stylesheet"
  href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>

3.3 图标设置

npm install @mui/icons-material
<link
  rel="stylesheet"
  href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>

3.4 最简示例

import * as React from 'react';
import Button from '@mui/material/Button';

export default function App() {
  return (
    <Button variant="contained" color="primary">
      Hello Material UI
    </Button>
  );
}

4. 主题定制深度指南

4.1 创建主题

import { createTheme } from '@mui/material/styles';

const theme = createTheme({
  // 颜色配置
  palette: {
    mode: 'dark', // 或 'light'
    primary: {
      main: '#90caf9',
      light: '#e3f2fd',
      dark: '#42a5f5',
    },
    secondary: {
      main: '#ce93d8',
    },
    error: {
      main: '#f44336',
    },
    warning: {
      main: '#ff9800',
    },
    info: {
      main: '#2196f3',
    },
    success: {
      main: '#4caf50',
    },
  },

  // 字体配置
  typography: {
    fontFamily: '"Inter", "Roboto", sans-serif',
    h1: { fontSize: '2.5rem', fontWeight: 600 },
    h2: { fontSize: '2rem', fontWeight: 600 },
  },

  // 间距配置(基线 8px)
  spacing: 8,

  // 圆角
  shape: {
    borderRadius: 8,
  },

  // 阴影
  shadows: [
    'none', // shadow-0
    '0px 2px 1px -1px rgba(0,0,0,0.2),...', // shadow-1
    // ... 更多阴影
  ],
});

4.2 主题嵌套

支持在同一应用中混合使用多个主题:

<ThemeProvider theme={lightTheme}>
  <Button>Light Button</Button>
  <ThemeProvider theme={darkTheme}>
    <Button>Dark Button (nested)</Button>
  </ThemeProvider>
</ThemeProvider>

4.3 组件级覆盖

// 全局覆盖
const theme = createTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: 20,
        },
      },
    },
    MuiCard: {
      styleOverrides: {
        root: {
          boxShadow: '0 4px 20px rgba(0,0,0,0.1)',
        },
      },
    },
  },
});

// 局部覆盖
<Button
  sx={{
    borderRadius: '20px',
    px: 4,
  }}
>
  Custom Button
</Button>

4.4 暗色模式

import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

function App() {
  const [mode, setMode] = React.useState<'light' | 'dark'>('light');

  const theme = React.useMemo(
    () =>
      createTheme({
        palette: {
          mode,
        },
      }),
    [mode],
  );

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline /> {/* 重置 CSS,提供一致的样式 */}
      <Switch onChange={() => setMode(mode === 'light' ? 'dark' : 'light')} />
      <Button>Hello</Button>
    </ThemeProvider>
  );
}

5. 核心组件实战

5.1 Button(按钮)

import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
import IconButton from '@mui/material/IconButton';
import Fab from '@mui/material/Fab';

// 普通按钮
<Button variant="text">Text</Button>
<Button variant="contained">Contained</Button>
<Button variant="outlined">Outlined</Button>

// 颜色
<Button color="primary">Primary</Button>
<Button color="secondary">Secondary</Button>
<Button color="error">Error</Button>

// 尺寸
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>

// 禁用
<Button disabled>Disabled</Button>

// 加载状态
<Button loading>Loading</Button>

// 图标按钮
<IconButton>
  <Icon>delete</Icon>
</IconButton>

// FAB(浮动操作按钮)
<Fab color="primary" aria-label="add">
  <AddIcon />
</Fab>

// 按钮组
<ButtonGroup variant="contained">
  <Button>One</Button>
  <Button>Two</Button>
  <Button>Three</Button>
</ButtonGroup>

5.2 TextField(文本输入)

import TextField from '@mui/material/TextField';

// 基本用法
<TextField label="Name" variant="outlined" />

// 密码输入
<TextField
  type="password"
  label="Password"
  autoComplete="current-password"
/>

// 多行文本
<TextField
  label="Description"
  multiline
  rows={4}
  variant="filled"
/>

// 辅助文本
<TextField
  label="Email"
  helperText="请输入有效的邮箱地址"
  error
/>

// 搜索框
<TextField
  InputProps={{
    startAdornment: <InputAdornment>search</InputAdornment>,
  }}
/>

// 选择框
<FormControl>
  <InputLabel>Age</InputLabel>
  <Select label="Age">
    <MenuItem value={10}>Ten</MenuItem>
    <MenuItem value={20}>Twenty</MenuItem>
  </Select>
</FormControl>

5.3 Card(卡片)

import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import CardMedia from '@mui/material/CardMedia';

<Card sx={{ maxWidth: 345 }}>
  <CardMedia
    component="img"
    height="140"
    image="/static/images/cards/contemplative-reptile.jpg"
    alt="Contemplative Reptile"
  />
  <CardContent>
    <Typography variant="h5" component="div">
      Lizard
    </Typography>
    <Typography variant="body2" color="text.secondary">
      Lizards are a widespread group of squamate reptiles
    </Typography>
  </CardContent>
  <CardActions>
    <Button size="small">Share</Button>
    <Button size="small">Learn More</Button>
  </CardActions>
</Card>

5.4 Dialog(对话框)

import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogActions from '@mui/material/DialogActions';

const [open, setOpen] = React.useState(false);

<Button onClick={() => setOpen(true)}>Open Dialog</Button>

<Dialog open={open} onClose={() => setOpen(false)}>
  <DialogTitle>删除确认</DialogTitle>
  <DialogContent>
    <DialogContentText>
      确定要删除这个项目吗?此操作无法撤销。
    </DialogContentText>
  </DialogContent>
  <DialogActions>
    <Button onClick={() => setOpen(false)}>取消</Button>
    <Button onClick={() => setOpen(false)} autoFocus>
      确定
    </Button>
  </DialogActions>
</Dialog>

5.5 Data Table(数据表格)

import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';

<TableContainer component={Paper}>
  <Table>
    <TableHead>
      <TableRow>
        <TableCell>Dessert (100g serving)</TableCell>
        <TableCell align="right">Calories</TableCell>
        <TableCell align="right">Fat (g)</TableCell>
        <TableCell align="right">Carbs (g)</TableCell>
      </TableRow>
    </TableHead>
    <TableBody>
      {rows.map((row) => (
        <TableRow key={row.name}>
          <TableCell component="th" scope="row">{row.name}</TableCell>
          <TableCell align="right">{row.calories}</TableCell>
          <TableCell align="right">{row.fat}</TableCell>
          <TableCell align="right">{row.carbs}</TableCell>
        </TableRow>
      ))}
    </TableBody>
  </Table>
</TableContainer>

6. 进阶用法

6.1 自定义组件

import { styled } from '@mui/material/styles';

const CustomButton = styled(Button)(({ theme }) => ({
  backgroundColor: theme.palette.success.main,
  color: '#fff',
  padding: '10px 20px',
  '&:hover': {
    backgroundColor: theme.palette.success.dark,
  },
}));

<CustomButton>Custom Styled Button</CustomButton>

6.2 SX 属性

// 使用 sx 属性进行快速样式定制
<Box
  sx={{
    display: 'flex',
    flexDirection: 'column',
    p: 2,
    bgcolor: 'background.paper',
    borderRadius: 1,
    boxShadow: 3,
    '&:hover': {
      bgcolor: 'grey.100',
    },
  }}
>
  Content
</Box>

6.3 响应式设计

// 使用断点
<Box
  sx={{
    // 默认(xs)
    width: 1,
    // md 及以上
    '@media (min-width: 900px)': {
      width: '50%',
    },
  }}
>

// 使用 useMediaQuery
import useMediaQuery from '@mui/material/useMediaQuery';

function App() {
  const matches = useMediaQuery('(min-width:600px)');
  return <span>{matches ? 'Desktop' : 'Mobile'}</span>;
}

6.4 状态管理集成

// 与 React Context 集成
const ThemeContext = React.createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ChildComponent />
    </ThemeContext.Provider>
  );
}

function ChildComponent() {
  const theme = React.useContext(ThemeContext);
  return <div>Current theme: {theme}</div>;
}

7. 性能优化

7.1 Tree Shaking

Material UI 支持 Tree Shaking,确保只打包使用的组件:

// ✅ 推荐:直接导入
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';

// ❌ 不推荐:导入全部(会增加包体积)
import { Button, TextField } from '@mui/material';

7.2 延迟加载组件

import { lazy, Suspense } from 'react';

const LazyDialog = lazy(() => import('./LazyDialog'));

function App() {
  return (
    <Suspense fallback={<CircularProgress />}>
      <LazyDialog />
    </Suspense>
  );
}

7.3 样式计算优化

// 使用 CSS 变量而不是每次计算样式
<Button
  sx={{
    // 使用 CSS 变量
    '--my-color': 'primary',
    bgcolor: 'var(--my-color)',
  }}
>

8. 版本升级指南

8.1 v5 到 v6 主要变化

变化说明
Emotion 10 → Emotion 11样式引擎升级
Joy UI 移除合并到 MUI X
@mui/material 重构更好的 Tree Shaking
不再支持 React 17需要 React 18.2+

8.2 升级命令

# 升级到最新版本
npm install @mui/material@latest @emotion/react@latest @emotion/styled@latest

# 使用 codemod 进行自动化迁移
npx @mui/codemod@latest v6.0.0 <path>

8.3 常见迁移问题

// v5
<DialogActions disableSpacing>

// v6
<DialogActions sx={{ gap: 1 }}>

// v5
<Box sx={{ display: 'flex' }}>

// v6
<Box sx={{ display: 'flex' }}> {/* 基本相同 */}

// v5
<IconButton edge="end">

// v6
<IconButton edge="end"> {/* 基本相同 */}

9. 与同类框架对比

9.1 对比 Ant Design

维度Material UIAnt Design
设计语言Material DesignAnt Design
组件数量丰富非常丰富
学习曲线较平缓较陡峭
主题定制灵活灵活
国际化需要额外配置内置支持
文档质量优秀优秀

9.2 对比 Chakra UI

维度Material UIChakra UI
样式方案Emotion CSS-in-JSEmotion CSS-in-JS
TypeScript完整支持完整支持
组件数量更多相对较少
社区生态更大较小
版本稳定性非常稳定较新

10. 总结

Material UI 是 React 生态中最成熟、最广泛使用的 Material Design 实现:

为什么选择 Material UI:

优势说明
十年实战检验经过数千贡献者验证,稳定可靠
组件丰富涵盖所有常见 UI 场景
主题灵活深度定制 Material Design
TypeScript 优先完善的类型支持
庞大生态被 200 万+ 仓库使用

适用场景:

  • 企业级 React 应用
  • 需要 Material Design 风格的界面
  • 需要丰富组件库的项目
  • 需要深度定制的设计系统

不适用的场景:

  • 需要轻量级组件库(考虑 Chakra UI)
  • 不使用 Material Design 的产品(考虑 Tailwind UI)
  • 需要超快开发速度的原型(考虑 React Bootstrap)

附录:相关资源

  • GitHub:https://github.com/mui/material-ui
  • 官方文档:https://mui.com/material-ui/
  • 官方博客:https://mui.com/blog/
  • MUI X(高级组件):https://github.com/mui/mui-x
  • 社区示例:https://github.com/mui/material-ui/tree/master/examples