基于 React Router 的面包屑组件设计与实现

需求

在新企业版的设计中,导航栏中的面包屑需要具备以下功能:

  • 显示当前页面的路径
  • 面包屑节点支持显示为下拉菜单
  • 面包屑节点内容可动态更改

调研

由于项目时间比较紧,调研对象仅限于几个容易找到的同类方案,最终确定的候选设计方案有两个:

  • ant-design/breadcrumb: 传入 routes 路由配置对象和 itemRender 函数,根据当前路径从 routes 中匹配面包屑列表,然后用 itemRender 渲染每个面包屑节点。
  • react-breadcrumbs-dynamic: 在页面中主动引入 <BreadcrumbsItem> 组件设置面包屑节点内容,然后由 组件组合这些面包屑。

ant-design/breadcrumb

ant-design 的面包屑组件依赖 routes 路由配置对象,但新企业版项目并不是采用路由配置的形式集中管理路由的,如果要采用这种设计方案,那么就需要重构现有的路由代码,成本比较高。

react-breadcrumbs-dynamic

使用它需要多引入 react-through 依赖项,还得给每个页面添加 <BreadcrumbsItem> 组件。

设计

结合上述方案以及 Vue Router 的使用经验,可以设计出如下组件:

  • <RouterView>:功能类似于 Vue Router 的同名组件,基于当前的路由信息渲染对应的组件
  • <RouterBreadcrumb>:基于当前路由信息生成面包屑
  • <RouterBreadcrumbItem>:动态设置面包屑结点内容
  • <RouterStoreProvider>: 用于实现 <RouterBreadcrumbItem><RouterBreadcrumb> 之间的数据通信

<RouterView> 接受的路由配置格式为:

1
2
3
4
5
6
7
[
{
text: '页面标题',
path: 'path/to/page',
routes: []
}
]

以下是完整的示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import {
RouterView,
RouterBreadcrumb,
RouterBreadcrumbItem,
RouterStoreProvider
} from 'gitee-router-react';

function UserList() {
return (
<div>
<Link to='/users/root'>root</Link>
<Link to='/users/admin'>admin</Link>
</div>
);
}

function UserDetail({ username }) {
return (
<div>
<RouterBreadcrumbItem>{username}</RouterBreadcrumbItem>
Detail of {username}
</div>
);
}

const routes = [
{
text: '用户',
path: 'users',
routes: [
{
text: '列表',
path: '',
exact: true,
component: UserList
},
{
text: '详情',
path: ':username',
exact: true,
component: UserDetail,
props({ params }) {
return params;
}
}
]
}
]

function App() {
return (
<div className='app'>
<RouterStoreProvider>
<RouterBreadcrumb routes={routes} className='app-breadcrumb' />
<RouterView routes={routes} />
</RouterStoreProvider>
</div>
);
}

这段示例代码实现了一个用户列表页面,点击列表中的 root 用户链接后,面包屑会更新为:用户 / root

实现

需要实现的主要功能有:

  • 路由匹配
  • 路由配置中的路径转换
  • 动态更新面包屑结点

路由匹配

react-router 的源码目录中有个预览版的 react-router-config 包已经实现了静态路由配置的渲染支持和路由匹配,可以直接参考它的源码实现。

路由配置中的路径转换

这个功能很简单,大致的流程是递归遍历路由配置列表,然后转换每个路由配置的路径为绝对路径。

动态更新面包屑结点

<RouterStoreProvider> 组件维护一个映射表,以路由配置中的 path 为索引键,记录包括面包屑结点内容在内的状态,然后使用 React.Context 实现 <RouterBreadcrumbItem> 组件与 <RouterBreadcrumb> 组件的状态共享。

开发

新的路由方案实现起来比较简单,主要的工作量都集中在旧路由改造上,改造可以分为以下几个部分:

  • 将原有的 <Switch> + <Route> 使用方式改为 <RouterView>
  • 将分散在各个导航菜单、页面组件中的 <Route> 组件参数都集中到 routes 目录中,以新的路由配置表示
  • 给项目和仓库页面添加 <RouterBreadcrumbItem> 组件,向面包屑注册切换菜单
  • 给 PullRequest、里程碑等页面添加 <RouterBreadcrumbItem> 组件,动态更新面包屑内容

总结

由于新企业版项目采用的是全新的 React 技术栈,整个团队在 React 方面也没有积累多少开发经验,因此,在没有路由管理最佳实践方案可供使用的情况下,团队成员都是按照自己的风格管理页面的路由,随着新页面的增加以及各种新需求的实现,路由管理问题变得越来越明显,直到这次的面包屑导航组件为止,除了对它进行改造外已经没有别的办法了,因为考虑其它方案太费时间,大都会增加代码复杂度,导致代码会越改越烂。

现在采用的路由配置方案参考自 Vue Router,虽称不上是 React 路由管理的最佳实践,但也算是一个经历了众多 Vue 项目考验的成熟的方案,而且对于新企业版项目而言有以下好处:

  • 路由配置的格式简单且都集中在一个目录中,易于理解和维护。
  • <RouterView> 组件的功能以及路由配置格式与 Vue Router 相似,有过 Vue Router 使用经验的人很容易上手。
  • 能作为参数传给面包屑组件使用,无需编写复杂的代码手动控制面包屑内容,以后的动态标题也能基于路由配置和匹配结果来实现。
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×