react-router-dom
安装
npm install react-router-dom
yarn add react-router-dom
pnpm add react-router-dom
- BrowserRouter 使用 HTML5 提供的历史 API 来保持 UI 与 URL 同步;
- HashRouter 通过在URL中添加哈希片段来工作(#),从而不会触发页面刷新并能绕过某些服务器限制;
- MemoryRouter 不依赖于实际的位置历史记录;相反,它是完全存在于内存中的位置管理器 (服务端渲染);
- Route 负责匹配当前请求的 URL 并决定是否渲染对应组件,当请求路径与指定路径相匹配时,关联组件将会被渲染到页面上;
- Routes 则是一个容器组件,用来包裹多个 Route 实例,其主要作用在于优化性能以及提供更合理的嵌套结构支持,替代旧版中的 Switch 。
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
<BrowserRouter>
<Routes>
<Route path='/' element={<App />}></Route>
<Route path='/demo' element={<Demo />}></Route>
<Route path='*' element={ <404 /> }></Route>
</Routes>
</BrowserRouter>
NavLink、Link
本质 <a> 标签,to = '..' 表示返回上一级
| 组件 | 含义 |
|---|---|
| NavLink | 自带 active 属性(activeClassName) |
| Link | 创建可点击链接,这些链接会导航到不同的 URL 而不会重新加载页面 |
重定向
<Route path='/demo' element={ <Navigate to={'/'} /> }></Route>
function navigate() {
return <h2>{ <Navigate to={'/'} /> }</h2>
}
useNavigate
const navigate = useNavigate();
navigate("/home"); // 通过事件进行跳转
函数式 useRoutes
router.ts
function Router() {
const routes = useRoutes([
{
path: "/",
element: <App />,
},
{
path: "*",
element: <NotFound />,
},
]);
}
export default Router;
main.ts
import BaseRouter from './router'
import { BrowserRouter } from 'react-router-dom'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<BrowserRouter>
<BaseRouter />
</BrowserRouter>
)
create
createBrowserRouter、createHashRouter
import { createBrowserRouter } from "react-router-dom";
// 创建路由
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
},
{
path: "*",
element: <NotFound />,
},
]);
App.tsx
import { RouterProvider } from 'react-router-dom'
<RouterProvider router={router} />
基础路由
配置根地址:http://localhost:8080/app
const router = createBrowserRouter(
[
{
path: "/",
element: <App />,
},
],
{
basename: "/app",
},
);
动态路由
import { useParams } from "react-router-dom";
function Order() {
const params = useParams();
return <span>商品ID:{params.goodsId}</span>;
}
// 一级
{
path: '/order/:orderId',
element: <Order/>
}
// 二级
{
path: '/goods/:goodsId/order/:orderId',
element: <Order/>
}
嵌套路由
Outlet(相当于 Vue 里面的 route-view 组件)展示子组件
// 父组件定义
function Goods() {
return (
<div>
<h2>商品主页</h2>
<Outlet />
</div>
);
}
{
path: '/goods',
element: <Goods />,
children: [
{
path: 'list',
element: (
<div>
<p>商品一</p>
<p>商品二</p>
</div>
)
},
]
}
加载器
使用:createBrowserRouter、createMemoryRouter、createHashRouter、createStaticRouter Loader、useLoaderData:数据加载器、优先加载 loader 再加载 element
{
path: '/order/:id',
element: <Order />,
loader: orderLoader
}
function orderLoader({ params }: any) {
return params.id;
}
// 登录拦截
function orderLoader({ params }: any) {
if (!sessionStorage.token) return redirect('/login')
return {
token: sessionStorage.token
}
}
// 可以直接执行接口,请求 json 文件
function orderLoader({ params }: any) {
if (!sessionStorage.token) return redirect('/login')
return fetch(`/${params.id}.json`)
}
useActionData
文档:reactrouter.com/en/main/hoo…
React 分包加载优化:从串行到并行
React Router 6.4+ 数据路由的
lazy,把父渲染完才下载分包的串行流程,改成路由匹配即并行下载,性能提升约 50%。
一、传统分包为什么慢(串行瓶颈)
流程: 主包加载 → 父组件渲染(可能阻塞)→ 识别子路由 → 下载分包 JS → 渲染子组件
痛点: 父组件渲染完才去下载分包,一旦父组件有耗时逻辑(接口、计算),分包下载被严重阻塞,全程串行。
// 旧写法:React.lazy + Suspense
const Project = React.lazy(() => import("./Project"));
function Layout() {
// 假设这里阻塞 100ms
const [now] = useState(Date.now());
while (Date.now() - now < 100);
return (
<Suspense fallback={<div>Loading...</div>}>
<Project />
</Suspense>
);
}
二、6.4+ 数据路由优化方案(并行加载)
核心原理: 路由不再只绑定 UI,同时绑定异步包加载 —— 路由匹配后立刻并行下载分包,不用等父组件 render 完成。
import { createBrowserRouter, RouterProvider } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
index: true,
// 关键:路由级 lazy,自动并行下载
lazy: () => import("./Project"),
},
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
三、优化前后对比
| 维度 | 传统 React.lazy + Suspense | React Router 6.4+ 数据路由 |
|---|---|---|
| 加载模式 | 串行:父渲染 → 下分包 | 并行:父渲染与下分包同时跑 |
| 阻塞点 | 父组件阻塞 → 分包延迟下载 | 路由匹配即下载,无前置阻塞 |
| 耗时 | 约 140ms+ | 约 100ms 内,降 30%~50% |
| 代码 | 需手动 Suspense 包裹 | 路由配置 lazy,自动处理 |
四、为什么能快这么多
旧版: UI 渲染驱动包加载,必须等父组件渲染完才知道要加载谁。 新版: 路由匹配驱动包加载,匹配路径立刻并行下载,父组件渲染和包下载同时进行,消除串行等待。
五、话术
我做过分包极致优化:用 React Router 6.4+ 数据路由的
lazy,把传统父渲染完才下载分包的串行流程,改成路由匹配就并行下载,父组件渲染和分包下载同时跑,复杂场景性能提升约 50%。
六、最佳实践
| 策略 | 说明 |
|---|---|
| 数据路由标配 | 必须用 createBrowserRouter + RouterProvider |
| 路由级 lazy | 替代组件级 React.lazy,自动并行 |
| HTTP/2 多路复用 | 配合多路复用,分包更细更快 |
| 公共依赖抽离 | vendor 长期缓存,进一步提速 |