React使用Context与router实现权限路由详细介绍
绿胡子大叔 人气:0前言
之前使用高阶组件和Context实现简单的权限拦截,本篇文章将引入react-router
,实现权限路由功能
通过阅读本篇文章,你将了解:
- 如何在当前示例中使用
Context
,以及如何通过React Hooks
使用Context
- 创建一个
PermissionRoute
组件,强化原react-router-dom
库中的Route
组件 - 创建一个类似
react-router-config
中renderRoutes()
的函数,实现集中式权限路由配置
思路
- 创建一个高阶组件包裹根组件,使用高阶组件向根组件注入一个
Context
。此高阶组件用于保存和获取权限列表 - 通过与
Context
中的权限列表进行对比,判断是否有页面访问权限。实现渲染劫持,控制渲染路由组件或重定向组件
实现
向根组件注入权限列表
抽离Context
/* * 资源路径 ./src/utils/PermissionContext.js */ import { createContext } from "react"; const PermissionContext = createContext() export const PermissionContextProvider = PermissionContext.Provider export const PermissionContextConsumer = PermissionContext.Consumer export default PermissionContext
HOC实现
/* * 资源路径 ./src/components/PermissionIndex.js */ import React, { useState, useEffect } from 'react' import { PermissionContextProvider } from '../../utils/PermissionContext' // import对应的Context function PermissionIndex(Component) { return function Index(props){ const [permission, setpermission] = useState([]) useEffect(()=>{ setpermission(['cart']) //此处实际为 获取权限列表的请求操作 },[]) //代替了类组件的componenetDidMount生命周期 return ( <PermissionContextProvider value={permission}> <Component {...props}></Component> </PermissionContextProvider> //此处返回了注入权限列表Context的组件 ) } } export default PermissionIndex
useState
用于动态设置权限列表- 组件通过
Context.Provider
包裹。权限列表改变时,所有Context
的消费者组件更新
权限路由组件实现
实现
/* * 资源路径 ./src/components/PermissionRoute.js */ import {useContext} from "react"; import PermissionContext from "../utils/PermissionContext"; import {Redirect, Route} from "react-router-dom"; function PermissionRoute(props){ const context = useContext(PermissionContext) return ( context.indexOf(props.permission) > -1 ? <Route path={props.path} exact={props.exact} strict={props.strict} sensitive={props.sensitive} render={prop => props.render ? props.render({...prop}) : <props.component {...props}/>} /> : <Redirect from={'/props.path'} to={"/403"}/> ) } export default PermissionRoute;
- 使用
useContext
获取PermissionContext
- 利用
Array.prototype.indexOf()
方法,判断权限列表中是否存在由参数传入的权限值。若存在,则返回响应路由;若不存在,则返回Redirect
组件重定向到无权限页面
使用方法
/* * 资源路径 ./src/App.js */ import React from 'react'; import {Redirect, Route, Switch} from 'react-router-dom'; import Page1 from "./pages/Page1"; import Page2 from "./pages/Page2"; import Page3 from "./pages/Page3"; import Error from "./pages/Error"; // 无页面时显示的404页面 import PermissionIndex from "./components/PermissionIndex"; import PermissionRoute from "./components/PermissionRoute"; import NoPermission from "./pages/NoPermission"; // 无权限时显示的页面 /* * 利用es7的decorator实现高阶组件 * 此处等价于: * class App extends React.Component { * render(){} * } * const App = PermissionIndex(App) * export default App */ @PermissionIndex export default class App extends React.Component{ render() { return ( <div> <Switch> <Route path={'/page1'} component={Page1}/> <PermissionRoute path={'/page2'} component={Page2} permission={"cart"}/> <Route path={'/page3'} component={Page3}/> <Route path={'/404'} component={Error}/> <Route path={'/403'} component={NoPermission}/> <Redirect to={'/404'}/> </Switch> </div> ); } }
注意: 此处App组件已在index.js
中被BrowserRouter
组件包裹
/* * 资源路径 ./src/index.js */ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import {BrowserRouter} from "react-router-dom"; ReactDOM.render( <React.StrictMode> <BrowserRouter> <App></App> </BrowserRouter> </React.StrictMode>, document.getElementById('root') );
实现类似react-router-config的集中式权限路由配置
实现
/* * 资源路径 ./src/components/permissionRouterConfig.js */ import {Redirect, Route, Switch} from "react-router-dom"; import React, {useContext} from "react"; import PermissionContext from "../utils/PermissionContext"; // extraParams 绑定到路由组件的其他参数 // switchParams 绑定到Switch组件的其他参数 function permissionRouterConfig(routes, extraParams = {}, switchParams = {}){ // 此处返回一个React组件,但此处不属于高阶组件 return function PRC(){ const context = useContext(PermissionContext) return routes ? ( <Switch {...switchParams}> { // 遍历传入的routes数组 routes.map((route,index)=>( /* * 通过Array.prototype.indexOf() 方法判断权限是否在权限列表中 * 通过withPermission参数判断该路由是否需要进行权限拦截 */ (context.indexOf(route.permission) > -1 || !route.withPermission) ? <Route key={route.key || index} path={route.path} exact={route.exact} sensitive={route.sensitive} strict={route.strict} render={props=>route.render?(route.render({...props, ...extraParams})) : (<route.component {...props} {...extraParams}/>)} /> : <Redirect from={route.path} to={'/403'}/> // 当权限列表中存在权限或不需要权限拦截时,渲染路由组件;否则渲染重定向组件 )) } </Switch> ) : null } } export default permissionRouterConfig
使用方法
/* * 资源路径 ./src/App.js */ import React from 'react'; import {Redirect, Route, Switch} from 'react-router-dom'; import Page1 from "./pages/Page1"; import Page2 from "./pages/Page2"; import Page3 from "./pages/Page3"; import Error from "./pages/Error"; import PermissionIndex from "./components/PermissionIndex"; import NoPermission from "./pages/NoPermission"; import permissionRouterConfig from "./components/permissionRouterConfig"; const routes = [{ path: '/page1', component: Page1, withPermission: true, // withPermission为true,启用权限拦截 permission: "cart" // 访问该页面所需的权限名 },{ path: '/page2', render: props=><Page2 {...props}></Page2> },{ path: '/page3', component: Page3, withPermission: false // withPermission为false或undefined,不启用权限拦截 },{ path: '/404', component: Error },{ path: '/403', component: NoPermission },{ path: '/', render: ()=><Redirect to={'/404'}/> }] const PermissionRouter = permissionRouterConfig(routes) // 传入routes规则,返回一个包含Switch和Route的组件 @PermissionIndex export default class App extends React.Component{ render() { return ( <div> <PermissionRouter/> </div> ); } }
加载全部内容