react-router-dom v6相比于v5变动很大,单独列出变动点觉得没有必要,可以当成全新的插件去学习并使用。官方文档的内容太多,这里整理一些平时常用的,降低入门门槛。
前提:使用 vite 新建React项目(后面我会出个相关教程的文章)
一、安装
csharp
yarn add react-router-dom
目前版本:"react-router-dom": "^6.18.0"
二、基本使用
新建pages/login.tsx
javascript
const Login = () => {
return <div>登录页</div>;
};
export default Login;
新建pages/home.tsx
javascript
const Home = () => {
return <div>home页</div>;
};
export default Home;
1. 定义路由
新建router/routes.tsx
javascript
/* eslint-disable react-refresh/only-export-components */
import { lazy } from 'react';
const Login = lazy(() => import('../pages/login'));
const Home = lazy(() => import('../pages/home'));
const routes = [
{
path: '/login',
element: <Login />,
},
{
path: '/',
element: <Home />,
},
];
export default routes;
新建router/index.tsx
javascript
import { createBrowserRouter } from 'react-router-dom';
import routes from './routes';
//可传第二个参数,配置base路径 { basename: "/app"}
const router = createBrowserRouter(routes);
export default router;
2. 使用路由
修改入口main.tsx
javascript
import { Suspense } from 'react';
import { RouterProvider } from 'react-router-dom';
import ReactDOM from 'react-dom/client';
import router from './router';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<Suspense fallback={<div>Loading...</div>}>
<RouterProvider router={router} />
</Suspense>,
);
在Suspense组件中渲染lazy组件,Suspense组件置于懒加载组件之上的任何位置;如果有嵌套路由,则需再使用Suspense组件包裹
三、嵌套路由
新建pages/user.tsx
javascript
const User = () => {
return <div>用户页</div>;
};
export default User;
新建pages/manage.tsx
javascript
const Manage = () => {
return <div>管理页</div>;
};
export default Manage;
1. 定义路由
修改router/routes.tsx
javascript
//新增
const User = lazy(() => import('../pages/user'));
const Manage = lazy(() => import('../pages/manage'));
//修改
const routes = [
{
path: '/login',
element: <Login />,
},
{
element: <Home />,
children: [
{
path: '/',
element: <User />,
},
{
path: '/manage',
element: <Manage />,
},
],
},
];
2. 配置嵌套路由路口
- 安装styled-components,一个针对React的CSS-in-JS库,用来编写组件样式
csharp
yarn add styled-components
- 修改pages/home.tsx
javascript
import { Suspense } from 'react';
import { Outlet, NavLink } from 'react-router-dom';
import styled from 'styled-components';
const Home = () => {
return (
<Wrapper>
<div className="header"></div>
<div className="main">
<aside>
<div className="menu_item">
<NavLink to="/" end>
user
</NavLink>
</div>
<div className="menu_item">
<NavLink to="/manage" end>
manage
</NavLink>
</div>
</aside>
<section>
<Suspense fallback={<div>Loading...</div>}>
<Outlet />
</Suspense>
</section>
</div>
</Wrapper>
);
};
export default Home;
const Wrapper = styled.div`
.header {
height: 60px;
border: 1px solid;
}
.main {
height: calc(100vh - 60px);
display: flex;
aside {
width: 260px;
border: 1px solid;
.active {
color: red;
}
}
section {
flex: 1;
}
}
`;
Outlet组件呈现匹配的子路由,类似vue的router-view组件
四、配置404页面
修改router/routes.tsx
javascript
const routes = [
//...
{
path: '*',
element: <div>404</div>,
},
]
404路由放在路由配置表的最后
五、声明式、编程式导航
1. 声明式导航
pages/home.tsx
ini
<NavLink to="/manage" end>manage</NavLink>
激活时,默认类名为active。end表示精准匹配,否则当访问/manage/123时,类名也为active
2. 编程式导航
修改pages/user.tsx
javascript
import { useNavigate } from 'react-router-dom';
const User = () => {
const navigation = useNavigate();
return (
<div>
user
<button onClick={() => navigation('/manage')}>manage</button>
</div>
);
};
export default User;
六、路由传参
新建pages/file.tsx
arduino
const File = () => {
return <div>File</div>;
};
export default File;
1. 动态路由匹配
- 定义动态路由
修改router/routes.tsx
javascript
//新增
const File = lazy(() => import('../pages/file'));
const routes = [
//...
{
element: <Home />,
children: [
//...
//新增
{
path: '/file/:id?',
element: <File />,
},
],
},
];
- 定义路由跳转
修改pages/home.tsx
ini
<NavLink to="/file/123" end>
file
</NavLink>
- 获取参数
修改pages/file.tsx
javascript
import { useParams } from 'react-router-dom';
const File = () => {
const { id } = useParams();
return <div>{id}</div>;
};
export default File;
2. search传参
1.定义路由
修改router/index.tsx
arduino
{
path: "/file",
element: <File />,
},
- 编程式传参
xml
<NavLink to={{ pathname: '/file', search: '?sort=name&id=2' }} end>
file
</NavLink>
- 命令式传参
css
<button onClick={() => navigation({ pathname: '/file', search: '?sort=name&id=2' })}>file</button>
- 获取参数
dart
import { useSearchParams } from 'react-router-dom';
const File = () => {
const [searchParams] = useSearchParams();
return (
<div>
获取id:{searchParams.get('id')}
获取sort:{searchParams.get('sort')}
</div>
);
};
export default File;
3. state传参
1.定义路由
arduino
{
path: "/file",
element: <File />,
},
- 编程式传参
xml
<NavLink to="/file" state={{ id: 1 }} end> file</NavLink>
- 命令式传参
css
<button onClick={() => navigation("/file", { state: { id: "2" } })}> file</button>
- 获取参数
javascript
import { useLocation } from "react-router-dom";
const File = () => {
const { state } = useLocation();
return <div>{state.id}</div>;
};
export default File;
特点:参数不会显示在路径上
七、错误处理
在路由组件加载过程中发生错误时展示的元素
- 新建pages/errorBoundary.tsx
javascript
import { useRouteError } from 'react-router-dom';
const ErrorBoundary = () => {
const error = useRouteError();
//错误信息,可用来错误上报
console.log(error);
return <>错误页面</>;
};
export default ErrorBoundary;
- 修改router/index.tsx
javascript
import ErrorBoundary from "../pages/errorBoundary";
const routes = [
//...
{
element: <Home />,
errorElement: <ErrorBoundary />, //挂载到父路由上
},
]
- 修改pages/router.tsx,使其报错
typescript
import { useNavigate } from "react-router-dom";
const obj: any = {
a: "2",
};
const User = () => {
const navigation = useNavigate();
return (
<div>
{/* 报错:因为取不到c属性 */}
{obj.b.c}
user
<button onClick={() => navigation("/manage")}>manage</button>
</div>
);
};
export default User;
访问/user时,可以看到原先的user页面内容被替换为错误页面
提示:如果路由没有配置errorElement,则错误将冒泡到最近的父路由上
八、重定向
情景:当没有权限时,重定向到登录页
修改pages/manage.tsx
javascript
import { Navigate } from 'react-router-dom';
const Manage = () => {
if (!localStorage.getItem('token')) {
return <Navigate to="/login" replace={true} />;
}
return <div>manage</div>;
};
export default Manage;
replace={true}:进行重定向
九、loader,action
loader:在路由导航完成之前执行,类似于vue router的路由前置守卫
action:使用react-router-dom提供的Form表单,当表单提交时action会被触发
loader与action都是V6版本新增的内容,实际使用场景并不多,这里不再详细阐述。