前言
createBrowserRouter
是 React Router v6.4+
中用于创建浏览器端路由的核心函数,它引入了数据路由(data routing
)的概念,提供了更强大的路由功能,如数据预加载、表单处理等。
一、createBrowserRouter 的主要用途
- 创建路由配置:定义应用程序的路由结构
- 数据预加载:在渲染组件前获取所需数据
- 表单处理:处理表单提交和数据变更
- 错误处理:提供路由级错误边界
- 嵌套路由:支持复杂布局和嵌套路由结构
- 延迟加载:实现代码分割和按需加载
二、完整代码示例
2.1、 基础路由配置
// src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import {
createBrowserRouter,
RouterProvider,
Link,
Outlet
} from 'react-router-dom';
// 页面组件
function Home() {
return (
<div className="page">
<h1>首页</h1>
<p>欢迎来到我们的网站!</p>
<nav className="page-nav">
<Link to="/products" className="nav-link">产品列表</Link>
<Link to="/about" className="nav-link">关于我们</Link>
</nav>
</div>
);
}
function About() {
return (
<div className="page">
<h1>关于我们</h1>
<p>我们是一家专注于前端技术的公司。</p>
<Link to="/" className="back-link">返回首页</Link>
</div>
);
}
// 产品列表组件
function Products() {
return (
<div className="page">
<h1>产品列表</h1>
<ul className="product-list">
<li><Link to="/products/1">产品 1</Link></li>
<li><Link to="/products/2">产品 2</Link></li>
<li><Link to="/products/3">产品 3</Link></li>
</ul>
<Link to="/" className="back-link">返回首页</Link>
</div>
);
}
// 布局组件
function RootLayout() {
return (
<div className="app">
<header className="app-header">
<h1 className="logo">React Router 示例</h1>
<nav className="main-nav">
<Link to="/" className="nav-item">首页</Link>
<Link to="/about" className="nav-item">关于</Link>
<Link to="/products" className="nav-item">产品</Link>
</nav>
</header>
<main className="app-content">
{/* 子路由将在这里渲染 */}
<Outlet />
</main>
<footer className="app-footer">
<p>© 2023 React Router 示例应用</p>
</footer>
</div>
);
}
// 创建路由配置
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
children: [
{
index: true, // 匹配根路径
element: <Home />
},
{
path: "about",
element: <About />
},
{
path: "products",
element: <Products />
}
]
}
]);
// 渲染应用
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
2.2、 高级功能:数据加载和表单处理
// 添加以下代码到 main.jsx
// 产品详情组件(带数据加载)
import { useLoaderData, useParams, Form, redirect } from 'react-router-dom';
function ProductDetail() {
const product = useLoaderData();
return (
<div className="page">
<h1>{product.name}</h1>
<p className="product-description">{product.description}</p>
<p className="product-price">价格: ¥{product.price}</p>
<div className="product-actions">
<Link to="/products" className="back-link">返回产品列表</Link>
{/* 删除产品表单 */}
<Form method="post" action={`/products/${product.id}/delete`}>
<button type="submit" className="delete-btn">删除产品</button>
</Form>
</div>
<div className="reviews-section">
<h2>用户评价</h2>
{/* 添加评价表单 */}
<Form method="post" action={`/products/${product.id}/review`}>
<div className="form-group">
<label htmlFor="rating">评分:</label>
<select id="rating" name="rating" required>
<option value="5">5 星</option>
<option value="4">4 星</option>
<option value="3">3 星</option>
<option value="2">2 星</option>
<option value="1">1 星</option>
</select>
</div>
<div className="form-group">
<label htmlFor="comment">评论:</label>
<textarea id="comment" name="comment" required></textarea>
</div>
<button type="submit" className="submit-btn">提交评价</button>
</Form>
</div>
</div>
);
}
// 产品数据加载器
export async function productLoader({ params }) {
// 模拟API请求
const products = {
1: { id: 1, name: 'React 教程', description: '深入学习 React 框架', price: 99 },
2: { id: 2, name: 'Node.js 实战', description: '构建高性能后端应用', price: 129 },
3: { id: 3, name: 'TypeScript 指南', description: '掌握类型安全的JavaScript', price: 89 }
};
// 模拟延迟
await new Promise(resolve => setTimeout(resolve, 500));
const product = products[params.productId];
if (!product) {
throw new Response('产品未找到', { status: 404 });
}
return product;
}
// 删除产品操作
export async function deleteProductAction({ params }) {
// 在实际应用中,这里会调用API删除产品
console.log(`删除产品 ${params.productId}`);
// 重定向到产品列表
return redirect('/products');
}
// 添加评价操作
export async function addReviewAction({ request, params }) {
const formData = await request.formData();
const review = {
productId: params.productId,
rating: formData.get('rating'),
comment: formData.get('comment'),
date: new Date().toISOString()
};
// 在实际应用中,这里会保存评价到数据库
console.log('添加评价:', review);
// 重定向回产品详情页
return redirect(`/products/${params.productId}`);
}
// 错误页面组件
function ErrorPage() {
return (
<div className="error-page">
<h1>出错了!</h1>
<p>抱歉,您访问的页面不存在或发生错误。</p>
<Link to="/" className="back-link">返回首页</Link>
</div>
);
}
// 更新路由配置
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
errorElement: <ErrorPage />, // 全局错误边界
children: [
{
index: true,
element: <Home />
},
{
path: "about",
element: <About />
},
{
path: "products",
element: <Products />,
children: [
{
path: ":productId",
element: <ProductDetail />,
loader: productLoader, // 数据预加载
errorElement: <div className="error">产品加载失败</div>, // 特定路由错误边界
children: [
{
path: "delete",
action: deleteProductAction // 删除操作
},
{
path: "review",
action: addReviewAction // 添加评价操作
}
]
}
]
}
]
}
]);
2.3、 延迟加载(代码分割)
// 更新路由配置,添加延迟加载
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
errorElement: <ErrorPage />,
children: [
{
index: true,
element: <Home />
},
{
path: "about",
// 延迟加载关于页面
lazy: () => import("./routes/About")
},
{
path: "products",
element: <Products />,
children: [
{
path: ":productId",
// 延迟加载产品详情
lazy: async () => {
const { default: ProductDetail } = await import("./routes/ProductDetail");
return {
Component: ProductDetail,
loader: productLoader
};
},
errorElement: <div className="error">产品加载失败</div>,
children: [
// ...操作路由
]
}
]
},
{
path: "dashboard",
// 延迟加载仪表盘
lazy: () => import("./routes/Dashboard")
}
]
}
]);
三、createBrowserRouter
核心功能详解
3.1、 路由配置结构
createBrowserRouter 接收一个路由对象数组,每个对象包含:
path
:路由路径element
:渲染的组件loader
:数据加载函数action
:表单处理函数errorElement
:错误边界组件children
:嵌套路由
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: "about", element: <About /> }
]
}
]);
3.2、 数据加载(loader
)
在组件渲染前获取数据:
{
path: "products/:id",
element: <ProductDetail />,
loader: async ({ params }) => {
const response = await fetch(`/api/products/${params.id}`);
if (!response.ok) throw new Error('产品未找到');
return response.json();
}
}
在组件中使用数据:
import { useLoaderData } from 'react-router-dom';
function ProductDetail() {
const product = useLoaderData();
return <h1>{product.name}</h1>;
}
3.3、 表单处理(action
)
处理表单提交:
{
path: "products/:id/review",
action: async ({ request, params }) => {
const formData = await request.formData();
const review = Object.fromEntries(formData);
// 保存评价
await saveReview(params.id, review);
// 重定向回产品页
return redirect(`/products/${params.id}`);
}
}
在表单组件中使用:
import { Form } from 'react-router-dom';
function ReviewForm() {
return (
<Form method="post" action="/products/123/review">
{/* 表单字段 */}
</Form>
);
}
3.4、 错误处理
{
path: "/",
element: <Layout />,
errorElement: <ErrorBoundary />, // 全局错误边界
children: [
{
path: "products/:id",
element: <ProductDetail />,
errorElement: <ProductError />, // 特定路由错误边界
loader: productLoader
}
]
}
在错误边界组件中获取错误信息:
import { useRouteError } from 'react-router-dom';
function ErrorBoundary() {
const error = useRouteError();
return <div>错误: {error.message}</div>;
}
3.5、 路由导航
import { Link, NavLink, useNavigate } from 'react-router-dom';
// 创建链接
<Link to="/about">关于我们</Link>
// 活动链接样式
<NavLink
to="/products"
className={({ isActive }) => isActive ? 'active' : ''}
>
产品
</NavLink>
// 编程式导航
function Home() {
const navigate = useNavigate();
return (
<button onClick={() => navigate('/about')}>
前往关于页面
</button>
);
}
四、createBrowserRouter
与传统路由对比
五、最佳实践
合理组织路由结构:使用嵌套路由处理复杂布局
利用代码分割:延迟加载非关键路由
统一错误处理:设置全局和局部错误边界
优化数据加载:
使用Promise.all并行加载数据
实现数据缓存
安全考虑:
验证用户输入
处理敏感数据
性能优化:
使用defer API处理慢速加载
实现加载状态指示器
总结
createBrowserRouter
是 React Router v6.4+ 中推荐的浏览器端路由创建方式,它通过数据驱动的路由模型提供了更强大的功能:
- 声明式路由配置:集中管理所有路由规则
- 内置数据加载:在渲染前预取所需数据
- 高效表单处理:简化表单提交逻辑
- 精细错误控制:路由级错误边界
- 无缝代码分割:优化应用性能
通过结合 RouterProvider
使用,可以构建功能丰富、性能优越的现代 React 应用程序,特别适合需要复杂数据管理和状态同步
的应用场景。