React 官方并没有提供对应的路由插件,因此,我们需要下载第三方的路由插件 —— React Router。
React Router 在 2021 年 11 月份的时候更新 v6 的版本。
1、下载路由
在项目根目录中,通过以下命令
yarn add react-router-dom
2、路由配置
1)首先在react项目的入口文件index.js文件中,使用
将
包裹起来
import {BrowserRouter} from 'react-router-dom'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
);
BrowserRouter
:包裹这个应用,一个React应用只需使用一次
在 React Router 中提供了两种路由模式:hash 和 history。
对应的的路由组件分别是:
实际使用时,任选其中一个模式引入即可
2)其次,在App.js文件中,使用
设置路由出口,使用
指定导航链接
import React from 'react'
import {Routes,Route} from 'react-router-dom'
import Login from './pages/Login'
import Register from './pages/Register'
import Home from './pages/Home'
export default function App() {
return (
}>
}>
}>
)
}
核心组件作用说明
Routes
:提供一个路由出口,满足条件的路由组件会渲染到组件内部Route
: 用于指定导航链接,完成路由跳转
path
:path属性指定匹配的路径地址element
element属性指定要渲染的组件React Router 中,路由的跳转分为两种方式:
1、通过Link组件跳转
import React from 'react'
import {Link} from 'react-router-dom'
export default function Login() {
return (
用户登录
没有账号,去注册
)
}
2、编程式路由跳转
实现步骤
import {useNavigate} from 'react-router-dom'
let navigate=useNavigate();
import React from 'react'
import {useNavigate} from 'react-router-dom'
export default function Register() {
const navigate=useNavigate()
const register=(e)=>{
e.preventDefault()
navigate('/login')
}
return (
)
}
注意
const register=(e)=>{
e.preventDefault()
navigate('/login',{replace:true})
}
1、基础配置
实现步骤
}>
}>
}>
}>
}>
export default function Home() {
return (
<>
{/* 二级路由出口 */}
>
)
}
2、默认二级路由设置
}>
}>
}>
{/*默认二级路由,添加index属性,删除掉path属性*/}
}>
}>
}>
3、404页配置
应用场景:当所有的路径都没有匹配的时候显示
语法说明:在各级路由的最后添加*
号路由作为兜底
}>
}>
}>
{/*默认二级路由,添加index属性,删除掉path属性*/}
}>
}>
}>
{/*当所有路径都没有匹配时渲染此路由*/}
}>
1、searchParams传参
实现步骤
import {useNavigate} from 'react-router-dom'
export default function CategroyList() {
let navigate=useNavigate();
return (
CategroyList
)
}
import {useSearchParams} from 'react-router-dom'
export default function CategoryDetail() {
let [params]=useSearchParams()
return (
CategroyDetail
ID:{params.get('id')}
)
}
2、params传参
实现步骤
}>
}>
import {useNavigate} from 'react-router-dom'
export default function CategroyList() {
let navigate=useNavigate();
return (
CategroyList
)
}
import React from 'react'
import {useParams} from 'react-router-dom'
export default function CategoryDetail() {
let params=useParams()
return (
CategroyDetail
ID:{params.id}
)
}
实现步骤
import Login from '../pages/Login'
import Register from '../pages/Register'
import Home from '../pages/Home'
import CategoryList from '../pages/Category'
import CategoryDetail from '../pages/Category/Detail'
import GoodsList from '../pages/Goods'
import Main from '../pages/Home/Main'
export default [
{
path:'/login',
element:
},
{
path:'/register',
element:
},
{
path:'/',
element: ,
children:[
{
index:true,
element:
},
{
path:'/categoryList',
element:
},
{
path:'/categoryDetail/:id',
element:
},
{
path:'/goodsList',
element:
}
]
}
]
import {useRoutes} from 'react-router-dom'
import router from './router/index'
function App() {
return useRoutes(router)
}
export default App;
包裹
root.render(
)
1、实现步骤
import {lazy} from 'react'
const Login=lazy(()=>import('../pages/Login'))
const Register=lazy(()=>import('../pages/Register'))
const Home=lazy(()=>import('../pages/Home'))
const CategoryList=lazy(()=>import('../pages/Category'))
const CategoryDetail=lazy(()=>import('../pages/Category/Detail'))
const GoodsList=lazy(()=>import('../pages/Goods'))
const Main=lazy(()=>import('../pages/Home/Main'))
export default [
{
path:'/login',
element:
},
{
path:'/register',
element:
},
{
path:'/',
element: ,
children:[
{
index:true,
element:
},
{
path:'/categoryList',
element:
},
{
path:'/categoryDetail/:id',
element:
},
{
path:'/goodsList',
element:
}
]
}
]
组件,来实现路由的懒加载。import {Suspense} from 'react'
function App() {
return(
loading>}>
{useRoutes(router)}
)
}
组件身上,必须设置一个 fallback
属性,属性值可以是一个 HTML 标签,也可以是一个自定义的组件。用于当路由组件还未加载出来前的提示。
2、解决路由闪屏
配置完路由懒加载后出现当进行路由跳转时,出现闪屏现象,要向解决这个问题可以使用 react-loadable插件进行解决
yarn add react-loadable
import Loadable from 'react-loadable';
export default function withLoadable(comp) {
return Loadable({
//懒加载组件页面
loader: comp,
loading: () => null,
delay: "",
})
}
import loadable from '../utils/loadable'
const Login=loadable(()=>import('../pages/Login'))
const Register=loadable(()=>import('../pages/Register'))
const Home=loadable(()=>import('../pages/Home'))
const CategoryList=loadable(()=>import('../pages/Category'))
const CategoryDetail=loadable(()=>import('../pages/Category/Detail'))
const GoodsList=loadable(()=>import('../pages/Goods'))
const Main=loadable(()=>import('../pages/Home/Main'))
export default [
{
path:'/login',
element:
},
{
path:'/register',
element:
},
{
path:'/',
element: ,
children:[
{
index:true,
element:
},
{
path:'/categoryList',
element:
},
{
path:'/categoryDetail/:id',
element:
},
{
path:'/goodsList',
element:
}
]
}
]
antd
是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。
官网地址:Ant Design - 一套企业级 UI 设计语言和 React 组件库
1、引入antd
yarn add antd
src/App.css
,在文件顶部引入 antd/dist/antd.css
。@import '~antd/dist/antd.css'
import React, { Component } from 'react'
import './assets/styles/index.scss';
import {Button} from 'antd'
export default class App extends Component {
render() {
return (
App
)
}
}
2、用户登录
import React from 'react'
import axios from 'axios'
import {useNavigate} from 'react-router-dom'
import {Form,Button, Input} from 'antd'
export default function Login() {
const navigate=useNavigate()
const login=async(value)=>{
const {data:{code,data:{token}}}=await axios.post("http://www.zhaijizhe.cn:3001/users/login",value)
if(code){
localStorage.setItem('token',token)
navigate("/home")
}
}
return (
账号 }
name="account"
rules={[
{
required: true,
message: '请输入账号',
}
]}>
密码
在asseets/css下,新建login.css,内容如下
.login{
display: flex;
justify-content: center;
align-items: center;
background-color: #001529;
height:100vh;
}
.login .ant-form{
width: 450px;
}
然后在assets/style/index.scss中引入login.scss
@import 'login.css';
@import '~antd/dist/antd.css'
3、后台首页布局设计
后台首页布局布局可分为三个部分,我们可以将其做成三个组件,分别是
3.1、头部组件
import React from 'react'
import { Layout, Menu } from 'antd';
const { Header } = Layout
export default function MyHeader() {
return (
蜗牛商城
)
}
关键代码分析
3.2、左侧栏菜单
import React from 'react'
import {Menu,Layout } from 'antd'
import { UserOutlined, LaptopOutlined, MailOutlined,UserSwitchOutlined } from '@ant-design/icons';
const { Sider } = Layout
export default function MySider() {
return (
)
}
3.3、右侧内容区域
import React from 'react'
import { Layout,Breadcrumb } from 'antd'
const {Content} = Layout
export default function MyContent() {
return (
首页
用户管理
用户列表
Content
)
}
在Home组件中引入这三个组件,如下
import React from 'react'
import MyHeader from './MyHeader';
import MySider from './MySider';
import MyContent from './MyContent';
import { Layout } from 'antd'
export default function Home() {
return (
)
}
4、完成安全退出功能
import React from 'react'
import { Layout, Menu,Popconfirm} from 'antd';
import {useNavigate} from 'react-router-dom'
const { Header } = Layout
export default function MyHeader() {
const navigate=useNavigate()
const logout=()=>{
localStorage.removeItem('token');
navigate('/login',{replace:true})
}
return (
蜗牛商城
)
}
5、菜单栏基本功能
5.1、配置路由
这里才使用集中式路由配置方式
import Register from "../views/Register";
import AuthComponent from '../component/AuthComponent'
import loadable from '../utils/loadable'
const Home=loadable(()=>import("../views/Home"))
const Login=loadable(()=>import("../views/Login"))
const Main=loadable(()=>import("../views/Main"))
const Product=loadable(()=>import("../views/Product"))
const ProductAdd=loadable(()=>import("../views/ProductAdd"))
const CategoryList=loadable(()=>import("../views/CategoryList"))
const RoleList=loadable(()=>import("../views/RoleList"))
const AccountList=loadable(()=>import("../views/AccountList"))
const AccountAdd=loadable(()=>import("../views/AccountAdd"))
export default [
{
path:'/login',
element:
},
{
path:'/register',
element:
},
{
path:'/home',
element: ,
children:[
{
index:true,
element:
},
{
path:'product/list',
element:
},
{
path:'shop',
element:
},
{
path:'product/category',
element:
},
{
path:'role',
element:
},
{
path:'user',
element:
},
{
path:'datav/flowers',
element:
}
]
}
]
然后再去修改App.jsx
import {useRoutes} from 'react-router-dom'
import {Suspense} from 'react'
import router from './router/index'
import './assets/css/app.css'
export default function App() {
return(
loading>}>
{useRoutes(router)}
)
}
5.2、设置二级路由出口
在MyContent.jsx中设置二级路由出口
import React from 'react'
import { Layout,Breadcrumb } from 'antd'
import {Outlet} from 'react-router-dom'
const {Content} = Layout
export default function MyContent() {
return (
首页
用户管理
用户列表
//设置二级路由出口
)
}
5.3、侧边栏设置路由跳转
import React from 'react'
import {Menu,Layout } from 'antd'
import {HomeOutlined, UserOutlined, LaptopOutlined, MailOutlined,UserSwitchOutlined } from '@ant-design/icons';
import {useNavigate} from 'react-router-dom'
const { Sider } = Layout
export default function MySider() {
const navigate=useNavigate()
return (
)
}
6、路由鉴权
路由鉴权就是能够实现未登录时访问拦截跳转到登录页
实现的思路:自己封装AuthComponent
路由鉴权高阶组件,实现未登录拦截,并跳转到登录页面
思路为:判断本地是否有token,如果有则返回子组件,否则就重定向到登录页
实现步骤
const TOKEN_KEY="giles_token";
const getToken=()=>localStorage.getItem(TOKEN_KEY)
const setToken=token=>localStorage.setItem(TOKEN_KEY,token)
const clearToken=()=>localStorage.removeItem(TOKEN_KEY)
const isAuth=()=>!!getToken()
export {isAuth,getToken,setToken,clearToken}
关键代码
import React,{useEffect} from 'react'
import {useNavigate} from 'react-router-dom'
import $http from '../api/http'
import {message} from 'antd'
export default function AuthComponent({children}) {
//判断token是否存在
const isAuth=!!localStorage.getItem('token')
const nav=useNavigate()
const getUserInfo=async()=>{
try {
await $http.users.getUserInfo()
} catch (error) {
message.warning('token失效请重新登录')
nav('/login',{replace:true})
}
}
useEffect(()=>{
getUserInfo()
},[])
if(isAuth){
return <>{children}>
}else{
message.warning('没有token请重新登录')
return <>{nav('/login')}>
}
}
在router/index.js中使用
包裹中
组件
export default [
{
path:'/login',
element:
},
{
path:'/register',
element:
},
{
path:'/',
element: ,
children:[
{
index:true,
element:
},
{
path:'/categoryList',
element:
},
{
path:'/categoryDetail/:id',
element:
},
{
path:'/goodsList',
element:
}
]
}
]
7、动态渲染侧边栏
首先在config文件夹下创建routes.js文件
import { UserOutlined, HomeOutlined,AppstoreOutlined,EuroCircleOutlined,DollarOutlined} from '@ant-design/icons';
const routes=[
{path:'/home',icon: ,title:'首页'},
{path:'/home/user',icon: ,title:'用户管理',roles:["超级管理员"]},
{path:'/home/role',icon: ,title:'角色管理',roles:["超级管理员"]},
{path:'/home/shop',icon: ,title:'店铺管理',roles:["超级管理员"]},
{
subMenuKey:'productManage',
icon: ,
subMenuTitle:'商品管理',
children:[
{path:'/home/product/list',icon: ,title:'商品列表'},
{path:'/home/product/category',icon: ,title:'分类列表'}
]
},
{
subMenuKey:'accountManage',
icon: ,
subMenuTitle:'统计管理',
children:[
{path:'/home/datav/flowers',icon: ,title:'市场数据'},
{path:'/home/datav/sale',icon: ,title:'销售数据'}
]
}
]
export default routes;
修改MySider.js文件
import React from 'react'
import { Menu, Layout } from 'antd'
import { useNavigate } from 'react-router-dom'
import routes from '../config/routes'
const { Sider } = Layout
export default function MySider() {
const navigate = useNavigate()
const renderMenu = (routes) => {
const { role } = JSON.parse(localStorage.getItem('userInfo'));
console.log('role',routes.filter((item) => !item.roles || item.roles.includes(role.name)));
return routes.filter((item) => !item.roles || item.roles.includes(role.name)).map((item) => {
const { path, title, subMenuKey, subMenuTitle, icon, children } = item;
if (!subMenuKey) {
return {title} ;
} else {
return
{renderMenu(children)}
}
});
}
return (
)
}
演示:分别以Giles/123456和xiaofei/666666两个用户登录,你会发现登录后的左侧栏菜单是不同的
8、Table表格渲染
8.1、普通列表展示
import React, { useEffect, useState } from 'react'
import $http from '../../api/http'
import {Table,Space,Button} from 'antd'
export default function ProductList() {
const [list, setList] = useState([])
const getProductList = async () => {
const { data: { data } } = await $http.products.findGoods()
console.log(data);
setList(data)
}
useEffect(() => {
getProductList()
}, [])
const columns = [
{
title: '商品名称',
dataIndex: 'name',
key: 'name',
},
{
title: '商品标题',
dataIndex: 'title',
key: 'title',
},
{
title: '商品价格',
dataIndex: 'price',
key: 'price',
},
{
title: '商品图片',
dataIndex: 'imgSrc',
key: 'imgSrc',
render:(item)=>{
return
}
},
{
title: '操作',
key: 'action',
render: (record) => (
),
}
];
return (
)
}
8.2、解决列表的key值问题
页面已经正常显示,但是查看控制台的时候,会报如下错误
Warning: Each child in a list should have a unique "key" prop.
这个问题非常经典,是由于在列表循环的时候,没有加入key属性出现的,那么在antd的Table中专门提供了一个rowKey这个属性来解决该问题,rowKey 表示表格行 key 的取值,那么这里边应该写上列表中元素对象的_id值即可
8.3、分页实现
const [list, setList] = useState([])
const [total,setTotal]=useState(0)
const [pageSize,setPageSize]=useState(0)
const getProductList = async (params={pageNo:1,pageSize:2}) => {
console.log('params',params);
const {data:{data,pageSize,total}} = await $http.products.findGoods(params)
setList(data)
setTotal(total)
setPageSize(pageSize)
}
设置pageination为false,并重新添加分页组件
return (
)
- 定义onChange函数,并在该方法中实现请求查询商品列表功能
const onChange=(pageNo,pageSize)=>{
getProductList({pageNo,pageSize});
}
9、树形菜单实现
import React,{useEffect,useState} from 'react'
import $http from '../../api/http'
import {Table} from 'antd'
export default function CategoryList() {
const [list,setList]=useState([])
const getCategroy=async()=>{
const {data:{data}}=await $http.products.findCategory()
console.log(data);
setList(data)
}
useEffect(()=>{
getCategroy()
},[])
const columns = [
{
title: '标签',
dataIndex: 'label',
key: 'label',
},
{
title: '值',
dataIndex: 'value',
key: 'value',
}
];
return (
)
}
你可能感兴趣的:(react.js,javascript,前端)