React+Material UI:提取Dashboard中AppBar与Drawer为可复用组件
提取Material UI Dashboard的AppBar和Drawer为可复用组件
步骤1:提取AppBar为独立组件
新建CustomAppBar.js文件,将原dashboard中的AppBar代码迁移过来,通过props传递侧边栏联动状态和触发方法:
import * as React from 'react'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import IconButton from '@mui/material/IconButton'; import MenuIcon from '@mui/icons-material/Menu'; import Typography from '@mui/material/Typography'; const drawerWidth = 240; export default function CustomAppBar({ open, handleDrawerOpen }) { return ( <AppBar position="fixed" sx={{ width: { sm: `calc(100% - ${drawerWidth}px)` }, ml: { sm: `${drawerWidth}px` }, }} > <Toolbar> <IconButton color="inherit" aria-label="open drawer" edge="start" onClick={handleDrawerOpen} sx={{ mr: 2, ...(open && { display: 'none' }) }} > <MenuIcon /> </IconButton> <Typography variant="h6" noWrap component="div"> Dashboard </Typography> </Toolbar> </AppBar> ); }
步骤2:提取Drawer为独立组件
新建CustomDrawer.js文件,迁移原Drawer代码,同样通过props接收状态和关闭方法,也可将菜单数据抽为props实现动态渲染:
import * as React from 'react'; import Drawer from '@mui/material/Drawer'; import List from '@mui/material/List'; import Divider from '@mui/material/Divider'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import InboxIcon from '@mui/icons-material/MoveToInbox'; import MailIcon from '@mui/icons-material/Mail'; import Toolbar from '@mui/material/Toolbar'; const drawerWidth = 240; export default function CustomDrawer({ open, handleDrawerClose }) { const drawerContent = ( <div> <Toolbar /> <Divider /> <List> {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( <ListItem key={text} disablePadding> <ListItemButton> <ListItemIcon> {index % 2 === 0 ? <InboxIcon /> : <MailIcon />} </ListItemIcon> <ListItemText primary={text} /> </ListItemButton> </ListItem> ))} </List> <Divider /> <List> {['All mail', 'Trash', 'Spam'].map((text, index) => ( <ListItem key={text} disablePadding> <ListItemButton> <ListItemIcon> {index % 2 === 0 ? <InboxIcon /> : <MailIcon />} </ListItemIcon> <ListItemText primary={text} /> </ListItemButton> </ListItem> ))} </List> </div> ); return ( <Drawer variant="temporary" open={open} onClose={handleDrawerClose} ModalProps={{ keepMounted: true }} sx={{ display: { xs: 'block', sm: 'none' }, '& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth }, }} > {drawerContent} </Drawer> ); }
步骤3:创建全局布局容器(推荐)
新建DashboardLayout.js,整合AppBar、Drawer和内容区域,作为所有页面的通用布局,避免重复维护状态:
import * as React from 'react'; import { useState } from 'react'; import Box from '@mui/material/Box'; import Toolbar from '@mui/material/Toolbar'; import Drawer from '@mui/material/Drawer'; import List from '@mui/material/List'; import Divider from '@mui/material/Divider'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import InboxIcon from '@mui/icons-material/MoveToInbox'; import MailIcon from '@mui/icons-material/Mail'; import CustomAppBar from './CustomAppBar'; import CustomDrawer from './CustomDrawer'; const drawerWidth = 240; export default function DashboardLayout({ children }) { const [open, setOpen] = useState(false); const handleDrawerOpen = () => setOpen(true); const handleDrawerClose = () => setOpen(false); const desktopDrawerContent = ( <div> <Toolbar /> <Divider /> <List> {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( <ListItem key={text} disablePadding> <ListItemButton> <ListItemIcon> {index % 2 === 0 ? <InboxIcon /> : <MailIcon />} </ListItemIcon> <ListItemText primary={text} /> </ListItemButton> </ListItem> ))} </List> </div> ); return ( <Box sx={{ display: 'flex' }}> <CustomAppBar open={open} handleDrawerOpen={handleDrawerOpen} /> <CustomDrawer open={open} handleDrawerClose={handleDrawerClose} /> {/* 桌面端永久侧边栏 */} <Drawer variant="permanent" sx={{ display: { xs: 'none', sm: 'block' }, '& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth }, }} open > {desktopDrawerContent} </Drawer> {/* 中央内容区域 */} <Box component="main" sx={{ flexGrow: 1, p: 3, width: { sm: `calc(100% - ${drawerWidth}px)` } }} > <Toolbar /> {children} </Box> </Box> ); }
步骤4:在页面中使用布局
任意页面只需嵌套在DashboardLayout中即可复用全局的AppBar和Drawer:
import DashboardLayout from './DashboardLayout'; export default function HomePage() { return ( <DashboardLayout> <h1>首页内容</h1> {/* 页面专属组件 */} </DashboardLayout> ); }
优化建议
- 状态全局化:如果需要跨页面控制侧边栏状态,可使用React Context或状态管理工具将
open状态提升到全局 - 动态菜单:将菜单数据作为props传入Drawer组件,支持不同角色展示不同导航项
- 常量复用:将
drawerWidth这类通用常量抽离到单独文件,方便统一修改
内容的提问来源于stack exchange,提问作者user1368974
相关产品推荐
相关产品推荐

