react 自定义高阶组件,实现路由拦截,子路由渲染
1、首先我们需要一个高阶组件“RoutingGuard.js”,和router.js(包含所以一级路由的一个数组)文件,在 app.js 中使用 高阶组件
(1)目录如下
2、RoutingGuard.js 高阶组件(这里需要注意的是 ('/' + pathname.split('/')[1]) === item.path.replace(/\s*/g,"") ,只检索一级路由)
import React, { Component } from "react"; import { Route, Redirect } from "react-router-dom"; import { getCookie } from '../utils/common.js' class FrontendAuth extends Component { render() { const { routerConfig, location } = this.props; console.log(this.props) const { pathname } = location; console.log( pathname.split('/')[1]) // 登录成功状态 const isLogin = getCookie("flwebvpn_admin_sessionid"); // 检索当前 pathname 是否包含,router.js 路面的某个路由 const targetRouterConfig = routerConfig.find( (item) => { // 这里为什么使用('/' + pathname.split('/')[1])检索路由,这一步是为了方便使用子路由,这样检索等于只检索一级路由,二级路由不检索 // 例如:/home/user 这里也会返回 home 组件,如果不想让 /home/user 路由展示home 组件,使其变成非法路由,就像在home组件添加 <Redirect from="/home/*" to="/404"></Redirect>,再或者可以在router.js 里面添加一个是否有子组件的状态,一级路由检索出来之后,判断该路由对象的子组件的状态 是否为true,如果为true返回路由对象,否则判断是否有2级路由有则为非法路由,返回false,没有2级路由返回路由对象
return ('/' + pathname.split('/')[1]) === item.path.replace(/\s*/g,"") } ); if (isLogin) { // 如果是登陆状态,想要跳转到登陆,重定向到主页 if (pathname === "/login" || pathname === '/') { return <Redirect to="/home" />; } else { // 如果路由合法,就跳转到相应的路由 if (targetRouterConfig) { return (<Route path={pathname} exact={true} component={targetRouterConfig.component} />); } else { // 如果路由不合法,重定向到 404 页面 return <Redirect to="/404" />; } } } else { // 没有登录 if(pathname === '/'){ // 没有登录,默认 / 路由跳转 /login 页面 return <Redirect to="/login" />; } else if (targetRouterConfig && !targetRouterConfig.auth) { // 合法路由 and (auth:false)该路由不需要登录 const { component } = targetRouterConfig; return <Route exact path={pathname} component={component} /> } else if (targetRouterConfig && targetRouterConfig.auth) { // 合法路由 and (auth:false)该路由需要登录 return <Redirect to="/login" />; } else { // 非登陆状态下,路由不合法时,重定向至 404 return <Redirect to="/404" />; } } } } export default FrontendAuth
3、router.js 包含所有一级路由的对象
import Home from '../page/home/home'; import Login from '../page/login/login.jsx'; import NotFound from '../page/NotFound/NotFound.jsx'; var routes = [ { path: "/home", name: "home", component: Home, auth: true }, { path: "/login", name: "login", component: Login, auth: false }, { path: "/404", name: "NotFound", component: NotFound, auth: false }, ] // auth 是否需要登录 export default routes;
4、app.js 使用高阶组件
import React from 'react'; import { HashRouter as Router,Switch } from "react-router-dom"; import router from './router/router'; import RoutingGuard from './router/RoutingGuard'; // 高阶组件 function App() { return ( <div className="App"> <Router> <Switch> // 使用高阶组件,传入router 数组 <RoutingGuard routerConfig={router} /> </Switch> </Router> </div> ); } export default App;
5、home 组件实现 子路由
import React from "react"; import { Drawer, NavBar, Toast } from 'antd-mobile'; import './home.less'; import userManagement from '../../components/userManagement/userManagement.jsx'; import Theme from '../../components/theme/theme.jsx'; import roleManagement from '../../components/roleManagement/roleManagement.jsx'; import resourceAdministration from '../../components/resourceAdministration/resourceAdministration.jsx'; import { Route,Redirect,Switch } from "react-router-dom"; import { Button, WingBlank } from 'antd-mobile'; import { delCookie } from "../../utils/common"; export default class home extends React.Component { state = { docked: false, title: 'webVpn' } onDock = (d) => { this.setState({ [d]: !this.state[d], }); } // 导航切换 NavSelect = (routerPath, index) => { // 动画 this.refs.path.style.top = (document.getElementsByClassName('c-sidebar__link')[0].offsetHeight * index) + 'px'; // 路由 this.props.history.push(routerPath); setTimeout(() => { this.setState({ docked: false }) // 修改title if (this.props.location.pathname === '/home') { this.setState({ title: '主题' }) } else if (this.props.location.pathname === '/home/userManagement') { this.setState({ title: '用户管理' }) } else if (this.props.location.pathname === '/home/role') { this.setState({ title: '角色管理' }) } else if (this.props.location.pathname === '/home/resouce') { this.setState({ title: '资源管理' }) } }, 300); } componentDidMount() { if (this.props.location.pathname === '/home') { this.refs.path.style.top = (document.getElementsByClassName('c-sidebar__link')[0].offsetHeight * 0) + 'px'; this.setState({ title: '主题' }) } else if (this.props.location.pathname === '/home/userManagement') { this.refs.path.style.top = (document.getElementsByClassName('c-sidebar__link')[0].offsetHeight * 1) + 'px'; this.setState({ title: '用户管理' }) } else if (this.props.location.pathname === '/home/role') { this.refs.path.style.top = (document.getElementsByClassName('c-sidebar__link')[0].offsetHeight * 2) + 'px'; this.setState({ title: '角色管理' }) } else if (this.props.location.pathname === '/home/resouce') { this.refs.path.style.top = (document.getElementsByClassName('c-sidebar__link')[0].offsetHeight * 3) + 'px'; this.setState({ title: '资源管理' }) } } // 退出登录 logOut = ()=>{ delCookie('flwebvpn_admin_sessionid'); Toast.success('退出成功'); this.props.history.push('/login'); } render() { console.log(`${this.props.match.path}/user`) const sidebar = (<div className="navContent"> <p className="web-font logo">webVpn</p> <span className="c-sidebar__title"> 管理员操作 </span> <ul> <li className={this.props.location.pathname === '/home' ? 'c-sidebar__link activeNav' : 'c-sidebar__link'} onClick={ this.NavSelect.bind(this, '/home', 0) }> <i className="iconfont"></i> <span>主题</span> </li> <li className={this.props.location.pathname === '/home/userManagement' ? 'c-sidebar__link activeNav' : 'c-sidebar__link'} onClick={ this.NavSelect.bind(this, '/home/userManagement', 1) }> <i className="iconfont"></i> <span>用户管理</span> </li> <li className={this.props.location.pathname === '/home/role' ? 'c-sidebar__link activeNav' : 'c-sidebar__link'} onClick={ this.NavSelect.bind(this, '/home/role', 2) }> <i className="iconfont"></i> <span>角色管理</span> </li> <li className={this.props.location.pathname === '/home/resouce' ? 'c-sidebar__link activeNav' : 'c-sidebar__link'} onClick={ this.NavSelect.bind(this, '/home/resouce', 3) }> <i className="iconfont"></i> <span>资源管理</span> </li> <span ref="path"></span> </ul> <WingBlank style={{ position: 'absolute',left: '0',right:'0',bottom: '50px' }}> <Button size="large" onClick={ this.logOut }>退出登录</Button> </WingBlank> </div>); return ( <div className="home" style={{ height: '100%' }}> <Drawer className="my-drawer" style={{ minHeight: document.documentElement.clientHeight }} contentStyle={{ color: '#A6A6A6', textAlign: 'center' }} sidebarStyle={{ border: '1px solid #ddd' }} sidebar={sidebar} docked={this.state.docked} touch={true} dragToggleDistance={30} > <NavBar mode="light" icon={ <i className="iconfont"></i> } onLeftClick={() => this.onDock('docked')} rightContent={[]} > <p className="web-font NavTitle">{this.state.title}</p> </NavBar> {/* 子路由 */} <Switch> <Route path='/home' exact component={Theme}></Route> <Route path='/home/userManagement' exact component={userManagement}></Route> <Route path='/home/role' exact component={roleManagement}></Route> <Route path='/home/resouce' exact component={resourceAdministration}></Route> {/* /home/*: /home下的子路由路由不合法时跳转404页面 */} <Redirect from="/home/*" to="/404"></Redirect> </Switch> </Drawer> </div> ) } }
大功告成
有什么不足的地方,还请大佬指出