面包屑在网页开发中非常重要,因为它们为用户提供了一种跟踪其在网页中当前位置的方法,并有助于网页导航。
在本文中,我们将使用 react-router v6 和 bootstrap 在 react 中实现面包屑。
react-router v6 是 react 和 react native 中使用的路由库,用于在网页或 web 应用程序中导航。
我们的实现使用 typescript,但它也可以轻松用于基于 javascript 的项目。
设置
首先,如果尚未安装的话,让我们在我们的项目中安装react-router-dom:
npm 安装react-router-dom
或者替代方案,使用纱线:
纱线添加react-router-dom
让我们也安装 bootstrap 来设计我们的组件:
npm 安装引导
实现我们的组件
然后我们创建一个 breadcrumbs.tsx 组件,它将包含面包屑的标记,还包括确定相对于根位置的当前位置的必要逻辑。
让我们首先为组件添加一个简单的标记:
<div classname="text-primary">
<nav aria-label="breadcrumb"><ol classname="breadcrumb">
<li classname="breadcrumb-item pointer">
<span classname="bi bi-arrow-left-short me-1"></span>
back
</li>
</ol></nav>
</div>
该组件目前只有一个后退按钮。让我们为后退按钮添加一个简单的实现,这样当单击时,应该加载上一页:
const goback = () => {
window.history.back();
};
下一步将编写一个函数,该函数将使用 matchroutes 函数来获取当前路由并应用转换来过滤出与当前路由相关的所有路由。
matchroute 接受 agnosticrouteobject 类型的对象数组并返回 agnosticroutematch[] | null 其中 t 是我们传入的对象的类型。
另外需要注意的是,该对象必须包含名为 path 的属性。
让我们首先为我们的路线声明一个接口:
interface iroute {
name: string;
path: string; //important
}
然后让我们声明我们的路线:
const routes: iroute[] = [
{
path: '/home',
name: 'home'
},
{
path: '/home/about',
name: 'about'
},
{
path: '/users',
name: 'users'
},
{
path: '/users/:id',
name: 'user'
},
{
path: '/users/:id/settings/edit',
name: 'edit user settings'
}
];
我们还声明了一个变量来保存 uselocation 钩子,还声明了另一个变量来保存面包屑的状态:
const location = uselocation();
const [crumbs, setcrumbs] = usestate<iroute>([]);
</iroute>
接下来,让我们实现我们的功能:
const getpaths = () => {
const allroutes = matchroutes(routes, location);
const matchedroute = allroutes ? allroutes[0] : null;
let breadcrumbs: iroute[] = [];
if (matchedroute) {
breadcrumbs = routes
.filter((x) => matchedroute.route.path.includes(x.path))
.map(({ path, ...rest }) => ({
path: object.keys(matchedroute.params).length
? object.keys(matchedroute.params).reduce(
(path, param) => path.replace(`:${param}`, matchedroute.params[param] as string), path)
: path,
...rest,
}));
}
setcrumbs(breadcrumbs);
};
在这里,我们首先获取与当前位置匹配的所有路线:
const allroutes = matchroutes(路线, 位置);
然后我们快速检查是否返回任何结果,并选择第一个:
常量匹配路由=所有路由? allroutes[0] : null;
接下来,我们过滤掉所有与当前路由匹配的路由:
routes.filter((x) =>matchedroute.route.path.includes(x.path))
然后让我们使用结果创建一个新数组,检查路径是否有参数,然后用参数值交换动态路由:
.map(({ path, ...rest }) => ({
path: object.keys(matchedroute.params).length
? object.keys(matchedroute.params).reduce(
(path, param) => path.replace(`:${param}`, matchedroute.params[param] as string),
path
)
: path,
...rest,
}));
这确保了如果我们在路由中将路由声明为 /users/:id/edit 并将 id 传递为 1,那么我们将得到 /users/1/edit。
接下来,让我们在 useeffect 中调用我们的函数,以便每次我们的位置发生变化时它都会运行:
useeffect(() => {
getpaths();
}, [location]);
完成此操作后,我们就可以在标记中使用面包屑:
{crumbs.map((x: iroute, key: number) =>
crumbs.length === key + 1 ? ({x.name}
) : (
{x.name}
) )}
在这里,显示所有的面包屑及其链接,除了最后一个仅显示名称的面包屑。
这样,我们现在就有了完整的 breadcrumbs.tsx 组件:
import { useEffect, useState } from 'react';
import { Link, matchRoutes, useLocation } from 'react-router-dom';
interface IRoute {
name: string;
path: string;
}
const routes: IRoute[] = [
{
path: '/home',
name: 'Home',
},
{
path: '/home/about',
name: 'About',
},
{
path: '/users',
name: 'Users',
},
{
path: '/users/:id/edit',
name: 'Edit Users by Id',
},
];
const Breadcrumbs = () => {
const location = useLocation();
const [crumbs, setCrumbs] = useState<iroute>([]);
const getPaths = () => {
const allRoutes = matchRoutes(routes, location);
const matchedRoute = allRoutes ? allRoutes[0] : null;
let breadcrumbs: IRoute[] = [];
if (matchedRoute) {
breadcrumbs = routes
.filter((x) => matchedRoute.route.path.includes(x.path))
.map(({ path, ...rest }) => ({
path: Object.keys(matchedRoute.params).length
? Object.keys(matchedRoute.params).reduce(
(path, param) => path.replace(`:${param}`, matchedRoute.params[param] as string),
path
)
: path,
...rest,
}));
}
setCrumbs(breadcrumbs);
};
useEffect(() => {
getPaths();
}, [location]);
const goBack = () => {
window.history.back();
};
return (
<div classname="">
<nav aria-label="breadcrumb"><ol classname="breadcrumb">
<li classname="breadcrumb-item pointer" onclick="{goBack}">
<span classname="bi bi-arrow-left-short me-1"></span>
Back
</li>
{crumbs.map((x: IRoute, key: number) =>
crumbs.length === key + 1 ? (
<li classname="breadcrumb-item">{x.name}</li>
) : (
<li classname="breadcrumb-item">
<link to="{x.path}" classname=" text-decoration-none">
{x.name}
</li>
)
)}
</ol></nav>
</div>
);
};
export default Breadcrumbs;
</iroute>
然后我们可以在应用程序的任何部分使用该组件,最好是在布局中。
结论
我们已经了解了如何实现一个简单的面包屑组件,我们可以将其添加到我们的应用程序中以改进导航和用户体验。
有用的链接
https://stackoverflow.com/questions/66265608/react-router-v6-get-path-pattern-for-current-route
https://medium.com/@mattywilliams/generating-an-automatic-breadcrumb-in-react-router-fed01af1fc3,这篇文章的灵感来自于此。