整个material ui的案例先整javascript的,此教程会包含react和mui的基础知识,后面有需要再搞搞typescript.
先贴一下文档
react官方中文文档:https://react.docschina.org/docs/getting-started.html
material ui官方文档:https://mui.com/zh/material-ui/getting-started/overview/
react 路由:https://react-router.docschina.org/
0.基础总结
虚拟dom:通过虚拟dom和真实dom的对比,减少渲染的内容,稍微注意key的使用
jsx:组件都写在jsx里面,普通js写在js文件里面,甚至css in js,彻底js化了,这也是和vue最大的区别
组件:一切皆组件,css,图片,文件都是组件,官方推荐用函数式组件
hook:官方推荐用函数式组件,那就必须使用钩子函数,useState,useEffect等钩子
路由:这个都查不多
redux:有必要吧,像vue的store
组件通信:props,状态提升,context,pubsub等
1.material ui是什么
material ui是半官方的react ui组件库,比antd使用的人更多,但是国内使用antd的人多。
Material UI 是一个开源的 React 组件库,它实现了 Google 的Material Design。
2.搭建react框架
npm install create-react-app yarn -g
create-react-app stack-mui
cd stack-mui
yarn start
会自动弹出浏览器访问http://localhost:3000/
3.安装mui
#安装mui和样式库
yarn add @mui/material @emotion/react @emotion/styled
#安装字体
yarn add @fontsource/roboto
#安装图标
yarn add @mui/icons-material
yarn add @mui/lab
安装redux
yarn add redux
yarn add react-redux
yarn add @reduxjs/toolkit
4.创建material ui基础组件
先创建一些基础页面组件用来测试,后面再慢慢完善这些基础组件,先全部显示出来
4.1创建主界面viewport
import React from 'react';
import Box from '@mui/material/Box';
import Navigation from '../navigation';
import MainTabs from '../mainTabs';
export default function Viewport() {
const [activeMenu, setActiveMenu] = React.useState({});
const transActiveMenu=(menu)=>{
setActiveMenu(menu);
}
return (
<Box sx={{display: 'flex', width: '100vw', height: '100vh'}}>
<Navigation transActiveMenu={transActiveMenu} style={{width: '250px', border: 'solid 1px #ddd'}}></Navigation>
<MainTabs activeMenu={activeMenu} style={{width: 'calc(100vw - 250px)'}}></MainTabs>
</Box>
);
}
然后在app中引用
import {BrowserRouter} from 'react-router-dom'
import logo from './logo.svg';
import './App.css';
import Viewport from './views/viewport';
import {menuSlice} from "./redux/store"
function App() {
return (
<BrowserRouter>
<Viewport></Viewport>
</BrowserRouter>
);
}
export default App;
4.2导航菜单
import React from 'react';
import MenuItem from "./MenuItem";
import List from '@mui/material/List';
import StarBorder from '@mui/icons-material/StarBorder';
import AppBar from '@mui/material/AppBar';
import Typography from '@mui/material/Typography';
import MenuIcon from '@mui/icons-material/Menu';
import IconButton from '@mui/material/IconButton';
import Toolbar from '@mui/material/Toolbar';
import AdbIcon from '@mui/icons-material/Adb';
import Box from '@mui/material/Box';
export default function Navigation(props) {
const {style,transActiveMenu}=props;
const menuData = [{
id: 1, title: "主页", icon: "MoveToInbox", url: "/"
}, {
id: 2, title: "系统管理", icon: "Drafts",
child: [{
id: 21, title: "用户管理", icon: "Send", url: "/system/user"
}, {
id: 22, title: "菜单管理", icon: "ExpandLess", url: "/system/menu"
}]
}, {
id: 3, title: "系统工具", icon: "ExpandMore",
child: [{
id: 31, title: "系统监控", icon: "StarBorder", url: "/tools/monitor",
child: [{
id: 131, title: "日志监控", icon: "Airplay", url: "/tools/monitor/LogMonitor"
}]
}]
}]
const [menuCollapse, setMenuCollapse] = React.useState({});
const [activeMenu, setActiveMenu] = React.useState(0);
const handlerClick = (menu) => {
menuCollapse[menu.id] = !menuCollapse[menu.id];
setMenuCollapse({...menuCollapse});
setActiveMenu(menu);
transActiveMenu(menu);
}
return (
<List sx={style}
component="nav"
aria-labelledby="nested-list-subheader"
subheader={
<Box>
<AppBar position="static">
<Toolbar variant="dense">
<AdbIcon sx={{display: {xs: 'none', md: 'flex'}, mr: 1}}/>
<Typography color="inherit" component="div" sx={{flexGrow: 1}}>
MUI案例系统
</Typography>
<div>
<IconButton edge="end" color="inherit" aria-label="menu">
<MenuIcon/>
</IconButton>
</div>
</Toolbar>
</AppBar>
</Box>
}
>
<MenuItem items={menuData} menuCollapse={menuCollapse} activeMenu={activeMenu} handlerClick={handlerClick}></MenuItem>
</List>
);
}
import React from 'react';
import {IconsMap} from "../../components/iconList";
import {ExpandMore,ExpandLess} from '@mui/icons-material';
import {Collapse,List,ListItemButton,ListItemText,ListItemIcon} from '@mui/material';
import {NavLink} from "react-router-dom";
function createMenuList(menuItems,menuCollapse,activeMenu,handlerClick,level){
return menuItems.map(currentMenu => {
const {id,title,icon,child,url} = currentMenu;
const toUrl=url?url:"/";
const IconTag=IconsMap[icon];
const hasChild=child && child.length>0;
const childMenus=hasChild?createMenuList(child,menuCollapse,activeMenu,handlerClick,level+1):null;
const listItem=(
<ListItemButton selected={id===activeMenu.id} sx={{pl: level*2}} onClick={() => handlerClick(currentMenu)}>
<ListItemIcon >
<IconTag></IconTag>
</ListItemIcon>
<ListItemText primary={title}/>
{hasChild?(menuCollapse[id]? <ExpandLess/> : <ExpandMore/>):<React.Fragment/>}
</ListItemButton>
)
return (
<React.Fragment key={id}>
{hasChild?listItem:
<NavLink style={{textDecoration: 'none', color: 'inherit'}} to={toUrl} >
{listItem}
</NavLink>
}
{hasChild &&
<Collapse in={menuCollapse[id]}>
<List component="div" disablePadding>
{[...childMenus]}
</List>
</Collapse>
}
</React.Fragment>
);
})
}
{/*<MenuList/>*/}
{/*<ListItemButton>*/}
{/* <ListItemIcon>*/}
{/* <SendIcon/>*/}
{/* </ListItemIcon>*/}
{/* <ListItemText primary="主页"/>*/}
{/*</ListItemButton>*/}
{/*<ListItemButton onClick={() => handlerCollapse("xtgl")}>*/}
{/* <ListItemIcon>*/}
{/* <StarBorder/>*/}
{/* </ListItemIcon>*/}
{/* <ListItemText primary="系统管理"/>*/}
{/* {menuCollapse["xtgl"] ? <ExpandLess/> : <ExpandMore/>}*/}
{/*</ListItemButton>*/}
{/*<Collapse in={menuCollapse["xtgl"]} timeout="auto" unmountOnExit>*/}
{/* <List component="div" disablePadding>*/}
{/* <NavLink style={{textDecoration: 'none', color: 'inherit'}} to="/system/user">*/}
{/* <ListItemButton sx={{pl: 4}} selected>*/}
{/* <ListItemIcon>*/}
{/* <AccountBoxIcon/>*/}
{/* </ListItemIcon>*/}
{/* <ListItemText primary="用户管理"></ListItemText>*/}
{/* </ListItemButton>*/}
{/* </NavLink>*/}
{/* <ListItemButton sx={{pl: 4}}>*/}
{/* <ListItemIcon>*/}
{/* <ReorderIcon/>*/}
{/* </ListItemIcon>*/}
{/* <ListItemText primary="菜单管理"/>*/}
{/* </ListItemButton>*/}
{/* </List>*/}
{/*</Collapse>*/}
{/*<ListItemButton onClick={() => handlerCollapse("xtgj")}>*/}
{/* <ListItemIcon>*/}
{/* <AirplayIcon/>*/}
{/* </ListItemIcon>*/}
{/* <ListItemText primary="系统工具"/>*/}
{/* {menuCollapse["xtgj"] ? <ExpandLess/> : <ExpandMore/>}*/}
{/*</ListItemButton>*/}
{/*<Collapse in={menuCollapse["xtgj"]} timeout="auto" unmountOnExit>*/}
{/* <List component="div" disablePadding>*/}
{/* <ListItemButton sx={{pl: 4}}>*/}
{/* <ListItemIcon>*/}
{/* <AdbIcon/>*/}
{/* </ListItemIcon>*/}
{/* <ListItemText primary="系统监控"/>*/}
{/* </ListItemButton>*/}
{/* <Collapse in={menuCollapse["xtgj"]} timeout="auto" unmountOnExit>*/}
{/* <List component="div" disablePadding>*/}
{/* <ListItemButton sx={{pl: 8}}>*/}
{/* <ListItemIcon>*/}
{/* <AdbIcon/>*/}
{/* </ListItemIcon>*/}
{/* <ListItemText primary="系统监控"/>*/}
{/* </ListItemButton>*/}
{/* </List>*/}
{/* </Collapse>*/}
{/* <ListItemButton sx={{pl: 4}}>*/}
{/* <ListItemIcon>*/}
{/* <AdbIcon/>*/}
{/* </ListItemIcon>*/}
{/* <ListItemText primary="系统监控"/>*/}
{/* </ListItemButton>*/}
{/* </List>*/}
{/*</Collapse>*/}
export default function MenuItem(props){
return createMenuList(props.items,props.menuCollapse,props.activeMenu,props.handlerClick,0);
}
4.3 maitabs
import React from "react";
import {Box,Tabs,Tab} from '@mui/material';
import TabContext from '@mui/lab/TabContext';
import TabPanel from '@mui/lab/TabPanel';
import Typography from '@mui/material/Typography';
import Dashboard from '../dashboard';
import Menu from '../system/menu';
import User from '../system/user';
import SystemMonitor from '../tools/systemMonitor';
import {Routes, Route} from 'react-router-dom'
import { useLocation } from "react-router";
import Close from '@mui/icons-material/Close';
import Send from '@mui/icons-material/Send';
export default function MainTabs(props) {
//props
const {style,activeMenu} = props;
//state
const [tabsValue, setTabsValue] = React.useState("1");
const [tabsList, setTabsList] = React.useState([
{title:"主页",id:1,url:"/",closeAble:false}
]);
const handleChange = (event, newValue) => {
setTabsValue(`${newValue}`);
};
//event
const location = useLocation();
React.useEffect(() => {
const url=location.pathname;
addTabByMenu(url);
}, [location]);
//function
const addTabByMenu=(url)=>{
if(activeMenu&&activeMenu.url===url){
const isExitTab=tabsList.some(tab=>tab.id===activeMenu.id)
const newTablsValue=`${activeMenu.id}`;
if(isExitTab){
if(tabsValue!==newTablsValue){
setTabsValue(newTablsValue);
}
}else{
tabsList.push({...activeMenu,closeAble:true})
setTabsList([...tabsList]);
setTabsValue(newTablsValue);
}
}
}
//todo 出问题了 菜单信息必须放全局,组件路由,否则路由刷新的时候找不到tab的名称
const closeTab = (tabId)=>{
tabsList.some((item,i)=>{
if(item.id===tabId){
tabsList.splice(i,1);
// setTabsValue(newTablsValue);
// setTabsList([...tabsList]);
}
})
}
return (
<Box sx={style}>
<TabContext value={tabsValue}>
<Box sx={{borderBottom: 1, borderColor: 'divider'}}>
<Tabs
value={tabsValue}
onChange={handleChange}
variant="scrollable"
scrollButtons="auto"
>
{/*<Tab label="主页" style={{minHeight:'48px'}} value="0" iconPosition="end" icon={<Close onClick={()=>{alert(1)}} fontSize="small"/>} />*/}
{
tabsList.map(oneTab=>{
return (<Tab key={oneTab.id} label={oneTab.title} value={`${oneTab.id}`} icon={oneTab.closeAble?<Close onClick={()=>{closeTab(oneTab.id)}} fontSize="small"/>:<React.Fragment />} style={{minHeight:'48px'}} iconPosition="end" />)
})
}
</Tabs>
</Box>
<TabPanel value="0"><Dashboard /></TabPanel>
</TabContext>
</Box>
);
}
看一下效果
代码结构
接下来我要使用路由,实现点击菜单,在tabs上添加对应的界面
5.react 路由
5.1安装路由组件
yarn add react-router-dom
4.2创建路由表
在src路径下面创建routes文件夹,然后下面创建index.jsx
import { useRoutes } from 'react-router-dom';
import Viewport from './views/Viewport';
import Dashboard from '../Dashboard';
import Menu from '../system/Menu';
import User from '../system/User';
import SystemMonitor from '../tools/SystemMonitor';
export default function Routes() {
return useRoutes([{
path: '/',
element: <Viewport />,
children: [{
path: 'dashboard',
element: <Dashboard />
},{
path: 'system',
children: [{
path: 'user',
element: <User />
},{
path: 'menu',
element: <Menu />
}]
},{
path: 'tools',
children: [{
path: 'systemMonitor',
element: <SystemMonitor />
}]
}]
}]);
}