Skip to main content

路由配置

简介

在单页面应用中,你可以通过显示或隐藏特定组件的显示部分来改变用户能看到的内容,而不用去服务器获取新页面。当用户执行应用任务时,他们要在你预定义的不同视图之间移动。在项目中我们使用的是 react-router

项目中路由组织方式说明

路由模式

React 在浏览器中存在两种路由模式:

1. BrowserRouter:浏览器的路由方式,使用的浏览器histrory api
2. HashRouter:在路径前加入#号成为一个哈希值,Hash模式的好处是,再也不会因为我们刷新而找不到我们的对应路径

因为 BrowserRouter 模式 需要后台做兼容(对于空路径做 try_files 设置)否则会找不到对应页面,所以目前项目中我们使用 HashRouter

定义路由基本结构

    // router/index.ts
export interface IURoute {
path: string; // 路径
redirect?: string; // 跳转链接
meta?: { // 路由携带的数据
title?: string; // 路由名称
icon?: React.FC; // 路由上的图标
data?: any; // 路由其他数据
};
component?: any; // 页面组建
children?: IURoute[]; // 子路由
}

路由组织方式

在 React 中路由也是一个组件 设置对应的路径展示对应的组件内容

菜单路由配置

菜单路由配置文件使用树形结构组织如下:

提取左侧菜单部分路由配置生成树形结构,便于多级菜单生成

// router/index.ts
export const Routes: IURoute[] = [
{
path: '/dashboard',
meta: {
title: '首页',
icon: DashboardOutlined
},
component: AsyncComponent(Dashboard)
},
{
path: '/settings',
meta: {
title: '菜单一',
icon: SettingOutlined
},
redirect: '/settings/content',
children: [
{
path: '/settings/content',
component: AsyncComponent(Content),
meta: {
title: 'content'
},
children: [
{
path: '/settings/content/add',
component: AsyncComponent(ContentAdd),
meta: {
title: 'content-add'
}
},
{
path: '/settings/content/:contentId',
component: AsyncComponent(ContentEdit),
meta: {
title: 'content-edit'
}
}
]
},
{
path: '/settings/users',
component: AsyncComponent(Users),
meta: {
title: 'users',
icon: UserSwitchOutlined
},
children: [
{
path: '/settings/users/add',
component: AsyncComponent(UsersAdd),
meta: {
title: 'users-add'
}
},
{
path: '/settings/users/:userId',
component: AsyncComponent(UsersEdit),
meta: {
title: 'users-edit'
}
}
]
},
]
}
];
循环树结构生成左侧菜单
     // src/components/sidebar/sidebar.tsx
<Menu theme="dark" mode="inline" defaultSelectedKeys={defaultSelectedKeys()} defaultOpenKeys={setOpenKeys()}>
{
Routes.map((Route: IURoute) => {
if (Route.children) {
return <SubMenu icon={ <Icon component={ Route?.meta?.icon } /> } key={ Route.path } title={ Route?.meta?.title }>{
Route.children.map((item: IURoute) => <Menu.Item onClick={() => handleClick(item.path) } key={ item.path }>{ item?.meta?.title }</Menu.Item> )
}</SubMenu>;
} else {
return <Menu.Item icon={ <Icon component={ Route?.meta?.icon } /> } onClick={ () => handleClick(Route.path) } key={ Route.path } >{ Route?.meta?.title }</Menu.Item>;
}
})
}
</Menu>

整体路由组件配置

所有路由组件采用扁平的数组模式展示,菜单部分路由组建需要使用treeToList工具函数从树结构转换为数组结构。

      // src/app.tsx
<Switch>
{
treeToList(Routes).map((route, index) =>
<Route
key={index}
exact
path={route.path}
render={ route.redirect ? () => <Redirect to={ route.redirect as string } /> : () => <Navigator children={ route.component() } />}
></Route>)
}
<Route path='/demo' component={Demo}></Route>
<Route path='/login' component={Login}></Route>
<Redirect exact to='/dashboard' from="/"></Redirect>
<Route path="**" component={NotFound}></Route>
</Switch>

面包屑数据

面包屑生成是监听路由变化,在菜单路由配置文件匹配到对应的路径后进行渲染

    // src/components/breadcrumb/breadcrumb.tsx
// 获取当前路径
const location = useLocation();

// 通过 treeFindPath 工具函数匹配出 当前的路由结构
const pathSnippets = treeFindPath(Routes, (item: IURoute) =>
item.path === location.pathname || matchPath(location.pathname, {
path: item.path,
exact: true,
strict: false
}),[]);

// 循环渲染 面包屑
const extraBreadcrumbItems = pathSnippets.map((item, index) => {
return (
<Fragment key={item.path}>
<Breadcrumb.Item>
{ index === 0 && <Icon component={item.meta?.icon} /> }
<Link to={item.path}>{ item.meta?.title }</Link>
</Breadcrumb.Item>
</Fragment>
);
})

路由命名和持久化规则

菜单路由

菜单的定义中包含菜单(menu)和菜单项(menu item)2 种实体。菜单是菜单项的分类,菜单项则负责连接具体页面。 XXX

设置
|-店铺设置
|-门店地址
|-员工管理
|-通用设置
  • 设置-通用设置,menu/:menu-item/setting/general
  • 设置-店铺设置-门店地址,menu/:menu/:menu-item/setting/store/address

默认路由

当菜单被激活时需直接路由到指定的菜单项,通常是该菜单下的第一个菜单项。

  • /setting 路由到 /setting/general
  • /setting/store 路由到 /setting/store/address

页面路由

实体管理(增、删、改、査)相关页面的路由规则。此规则通常运用于运营管理后台

规范

  • 多个单词之间用 - 分隔,如wiki模版:wiki-template
  • 路由中同一资源的不同状态用 Query 参数表述,如 /messages?status=read /messages?status=unread
消息
|-全部 /messages
|-未读消息 /messages?status=unread
|-已读消息 /messages?status=read
  • 路由中不同资源用不同路径表述,简历中的职历 /resumes/BNZD9LXCMMTEDIYW/career

实体管理页面路由(增删改査)

  • 列表页面 :entities,示例:/users
  • 详情页面 :entities/:id/detail,示例:/users/BNZD9LXCMMTEDIYW/detail
  • 新建页面 :entities/create,示例:/users/create
  • 编辑页面 :entities/:id,示例:/users/BNZD9LXCMMTEDIYW/edit
  • 列表页面中页卡 :entities/:tab:/users?type=disabled 无效用户页卡
  • 编辑页面中页卡 :entities/:id/:tab,示例:/users/BNZD9LXCMMTEDIYW/career
  • 详情页面中页卡 :entities/:id/detail/:tab,示例:/users/BNZD9LXCMMTEDIYW/detail/career

业务页面路由

  • 账户总览 account/home
  • 账户充值 account/recharge

其他页面路由 系统中还有其他各种各样的页面,这些页面基本可归为 2 类:展示性页面和操作性页面。展示型的路由路径用名字来呈现,操纵型页面通过动词来呈现。

类型示例
系统帮助页面/document/110015
控制台页面/dashboard
账户充值页面`/account/re