前言
本文主要介绍react-router一种实践方案,可以提供一种路由可配置与路由权限管理的实现思路。可以在codesandbox查看完成代码。
总结:
- 在react-router 中一切皆组件, 则 BrowserRouter 与 HashRouter 分别来实现history路由与hash路由.
Link 组件用于修改 url,Route 组件则用于展示 url 对应的组件path路径 匹配方式采用 path-to-regexp 库, 将字符串形式的path转换为一个正则表达式,然后将它与当前的位置进行匹配- 导航方式有 声明式
Link和 命令式useHistory - 参数传递, 可以用 state 或 url 上 query 的方式传参,
注意: state 传参 只在 history 模式下生效. - Route渲染优先级:
children > component > render, 其中render属性 是实现 路由拦截 的核心.
配置文件样例:
/**
author: 表示需要权限才能访问的路由
hide: 表示在菜单中不展示
root: 表示是只在第一层渲染
*/
const ROUTE_CONFIG = [
{
path: '/',
exact: 'true',
component: Home
},
{
path: '/list',
component: List,
author: true,
routes: [
{
path: '/list/detail',
author: true,
component: Detail
}
]
},
....
{
path: '/login',
hide: true,
root: true,
component: Login
},
{
path: '*',
hide: true,
root: true,
component: NotFoundPage
}
]
Menu实现
核心原理: 递归
/* hide 则不展示 */
function Menu({ routes }) {
return <ul>
{routes.map((item) => {
return item.hide ?
null :
<React.Fragment key={item.path}>
<MenuItem {...item} />
</React.Fragment>
})}
</ul>
}
function MenuItem({ path, component, routes, hide }) {
return hide ?
null :
(<li>
<Link to={path}>{component.name}</Link>
{routes?.length && <Menu routes={routes}></Menu>}
</li>)
}
RouterPage实现
通过递归, 实现嵌套路由
/*
author表示需要登录才能访问
root + level 实现只在第一层渲染
*/
function RouterPage({ routes, level = 0 }) {
const { auth } = useContext(AuthContext)
if (routes?.length) {
return <>
<Switch>
{
routes.map((item, i) => {
if (item.root && !level) {
return <Route path={item.path} exact={!!item.exact} key={i} component={item.component} />
}
return <Route path={item.path} exact={!!item.exact} key={i} render={() => {
return (auth || !item.author) ?
<>
<item.component />
<RouterPage routes={item.routes} level={level + 1} />
</> :
<Redirect to={{ pathname: '/login', state: { redirect: item.path } }} />
}} />
})
}
</Switch>
</>
}
return null
}
权限采用ContextAPI实现
核心API: React.createContext, React.useContext
ContextAPI 使用心得:
- React.createContext 的value属性类 似于 父组件的props派发数据
- React.useContext 就类似于从props中获取数据
const AuthContext = React.createContext()
const AuthRouterPage = () => {
const [auth, setAuth] = useState(false)
return <AuthContext.Provider value={{ auth, login: () => setAuth(true) }}>
<BrowserRouter >
{auth ?
<>
<h1>
<mark>User: Admin<button onClick={() => setAuth(false)}>logout</button></mark></h1>
</> :
null
}
<Menu routes={ROUTE_CONFIG}></Menu>
<RouterPage routes={ROUTE_CONFIG} />
</BrowserRouter>
</AuthContext.Provider>
}