react-acl-router
react-boilerplate-pro/src/app/init/router.js
react-boilerplate-pro/src/app/config/routes.js
权限管理作为企业管理系统中非常核心的一个部分,一直以来因为业务方很多时候无法使用准确的术语来描述需求成为了困扰开发者们的一大难题。这里我们先来介绍两种常见的权限管理设计模式,即基于角色的访问控制以及访问控制列表。
1、布局与路由
在讨论具体的布局组件设计前,我们首先要解决一个更为基础的问题,那就是如何将布局组件与应用路由结合起来。
下面的这个例子是 react-router 官方提供的侧边栏菜单与路由结合的例子,笔者这里做了一些简化:
const SidebarExample = () => ( <Router> <div style={{ display: "flex" }}> <div style={{ padding: "10px", width: "40%", background: "#f0f0f0" }} > <ul style={{ listStyleType: "none", padding: 0 }}> <li> <Link to="https://www.jb51.net/">Home</Link> </li> <li> <Link to="/bubblegum">Bubblegum</Link> </li> <li> <Link to="/shoelaces">Shoelaces</Link> </li> </ul> </div> <div style={{ flex: 1, padding: "10px" }}> {routes.map((route, index) => ( <Route key={index} path={route.path} exact={route.exact} component={route.main} /> ))} </div> </div> </Router> );
抽象为布局的思想,写成简单的伪代码就是:
<Router> <BasicLayout> // with sidebar {routes.map(route => ( <Route {...route} /> ))} </BasicLayout> </Router>
这样的确是一种非常优雅的解决方案,但它的局限性在于无法支持多种不同的布局。受限于一个 Router 只能包含一个子组件,即使我们将多个布局组件包裹在一个容器组件中,如:
<Router> <div> <BasicLayout> // with sidebar {routes.map(route => ( <Route {...route} /> )} </BasicLayout> <FlexLayout> // with footer {routes.map(route => ( <Route {...route} /> )} </FlexLayout> </div> </Router>
路由在匹配到 FlexLayout 下的页面时, BasicLayout 中的 sidebar 也会同时显示出来,这显然不是我们想要的结果。换个思路,我们可不可以将布局组件当做 children 直接传给更底层的 Route 组件呢?代码如下:
<Router> <div> {basicLayoutRoutes.map(route => ( <Route {...route}> <BasicLayout component={route.component} /> </Route> ))} {flexLayoutRoutes.map(route => ( <Route {...route}> <FlexLayout component={route.component} /> </Route> ))} </div> </Router>
这里我们将不同的布局组件当做高阶组件,相应地包裹在了不同的页面组件上,这样就实现了对多种不同布局的支持。还有一点需要注意的是, react-router 默认会将 match 、 location 、 history 等路由信息传递给 Route 的下一级组件,由于在上述方案中, Route 的下一级组件并不是真正的页面组件而是布局组件,因而我们需要在布局组件中手动将这些路由信息传递给页面组件,或者统一改写 Route 的 render 方法为:
<Route render={props => ( // props contains match, location, history <BasicLayout {...props}> <PageComponent {...props} /> </BasicLayout> )} />
另外一个可能会遇到的问题是, connected-react-router 并不会将路由中非常重要的 match 对象(包含当前路由的 params 等数据 )同步到 redux store 中,所以我们一定要保证布局及页面组件在路由部分就可以接收到 match 对象,否则在后续处理页面页眉等与当前路由参数相关的需求时就会变得非常麻烦。
2、页眉 & 页脚