在 React 单页应用(SPA)开发中,路由传参是实现页面间数据交互的核心功能。react-router-dom 提供了多种传参方式,开发者可以根据场景选择最合适的方案。其中有三种主流传参方法:查询字符串传参、动态路由参数传参和状态对象传参。
一、查询字符串传参(? + useSearchParams)
实现方式
通过 URL 的查询字符串(Query String)传递参数,参数以 ?key=value 的形式附加在 URL 后。使用 useSearchParams 钩子获取参数。
// 传参示例:跳转到 /home 并携带 category 参数
navigate('/home?category=1');
// 使用示例
// 接收参数
import { useSearchParams } from 'react-router';
const [searchParams] = useSearchParams(); // useSearchParams() 获取路由参数
const category = searchParams.get('category');
console.log(category); // 输出: "1"
优势
- 直观易用:参数直接暴露在 URL 中,便于调试和分享。
- 兼容性强:支持浏览器书签、直接访问和搜索引擎抓取。
- 多参数支持:可同时传递多个参数(如
?id=1&sort=desc)。
劣势
- 安全性低:敏感信息(如用户 token)不应通过 URL 传递。
- URL 长度限制:过长的参数可能导致性能问题或截断。
- 类型限制:参数值始终为字符串,需手动转换类型(如
Number(searchParams.get('id')))。
代码示例
// 传参组件
import { useNavigate } from 'react-router';
const ProductList = () => {
const navigate = useNavigate();
const handleSelect = (id) => {
navigate(`/product?productId=${id}`);
};
return (
<button onClick={() => handleSelect(123)}>查看产品</button>
);
};
// 使用示例
// 接收组件
import { useSearchParams } from 'react-router';
const ProductDetail = () => {
const [searchParams] = useSearchParams();
const productId = searchParams.get('productId');
return <div>产品ID: {productId}</div>;
};
二、动态路由参数传参(路径/:参数 + useParams)
实现方式
通过定义动态路由路径(如 /user/:id),将参数嵌入 URL 路径中。使用 useParams 钩子获取参数。
// 路由配置 -- 必须配置时加上 :params
<Route path="/user/:id" element={<UserProfile />} />
// 传参示例:跳转到 /user/123
navigate('/user/123');
// 接收参数
import { useParams } from 'react-router';
const { id } = useParams();
console.log(id); // 输出: "123"
优势
- RESTful 风格:符合资源导向的设计规范,如
/posts/123/comments/456。 - URL 美观:参数嵌入路径中,比查询字符串更简洁(如
/user/123vs/user?id=123)。 - SEO 友好:搜索引擎更易解析路径中的资源标识。
劣势
- 灵活性差:路径结构一旦定义,修改成本较高。
- 参数类型限制:同样仅支持字符串类型,需手动转换。
- 不支持复杂结构:无法传递嵌套对象或数组。
代码示例
// 路由配置
<Routes>
<Route path="/rgb/:r/:g/:b" element={<ColorComponent />} />
</Routes>
// 传参组件
import { useNavigate } from 'react-router';
const ColorPicker = () => {
const navigate = useNavigate();
const handleSelect = (r, g, b) => {
navigate(`/rgb/${r}/${g}/${b}`);
};
return (
<button onClick={() => handleSelect(255, 0, 0)}>红色</button>
);
};
// 接收组件
import { useParams } from 'react-router';
const ColorComponent = () => {
const { r, g, b } = useParams();
return (
<div style={{ backgroundColor: `rgb(${r},${g},${b})`, width: '100px', height: '100px' }}></div>
);
};
三、状态对象传参(state + useLocation)
实现方式
通过 navigate 的 state 选项传递参数,参数不会暴露在 URL 中。使用 useLocation 钩子获取参数。
// 传参示例:跳转到 /profile 并携带用户信息
navigate('/profile', {
state: { name: 'Alice', role: 'Admin' }
});
// 接收参数
import { useLocation } from 'react-router';
const location = useLocation();
const { name, role } = location.state;
console.log(name); // 输出: "Alice"
优势
- 安全性高:参数不显示在 URL 中,适合敏感数据。
- 支持复杂数据:可传递对象、数组等结构。
- 无 URL 长度限制:适用于大数据量传输。
劣势
- 不持久化:页面刷新后参数丢失(需配合本地存储或服务端保存)。
- 不支持书签:用户无法通过 URL 直接访问特定状态。
- 浏览器兼容性:部分旧浏览器可能不支持
history.state。
代码示例
// 传参组件
import { useNavigate } from 'react-router-dom';
const LoginForm = () => {
const navigate = useNavigate();
const handleLogin = () => {
navigate('/dashboard', {
state: { user: { id: 1, name: 'John' } }
});
};
return <button onClick={handleLogin}>登录</button>;
};
// 接收组件
import { useLocation } from 'react-router';
const Dashboard = () => {
const location = useLocation();
const { user } = location.state;
return <div>欢迎, {user.name}</div>;
};
四、方法对比与选型建议
| 特性 | 查询字符串(useSearchParams) | 动态参数(useParams) | 状态对象(useLocation.state) |
|---|---|---|---|
| 参数可见性 | ✅ 显示在 URL 中 | ✅ 显示在 URL 中 | ❌ 不暴露在 URL 中 |
| 安全性 | ❌ 敏感数据不安全 | ❌ 敏感数据不安全 | ✅ 更安全 |
| 数据类型支持 | 字符串 | 字符串 | 对象/数组 |
| 持久化能力 | ✅ 刷新后保留 | ✅ 刷新后保留 | ❌ 刷新后丢失 |
| 适用场景 | 分享链接、分页、排序 | 资源标识、RESTful API | 登录状态、临时数据传递 |
五、进阶技巧
1. 组合使用传参方式
在复杂场景中,可结合多种方式。例如,使用动态参数标识资源,同时通过查询字符串传递过滤条件:
navigate(`/products/123?sort=price&order=asc`);
2. 处理嵌套路由传参
在嵌套路由中,可通过 Outlet 组件传递共享参数:
const Parent = () => {
const { userId } = useParams();
return (
<div>
<h1>用户 {userId} 的详情</h1>
<Outlet /> {/* 子路由可自动获取父级参数 */}
</div>
);
};
3. 防止页面刷新丢失状态
若需持久化 state 传参,可结合 localStorage 或服务端存储:
// 传参时保存到 localStorage
navigate('/profile', {
state: { data: 'secret' }
});
localStorage.setItem('savedState', JSON.stringify({ data: 'secret' }));
// 页面刷新后恢复
const savedState = JSON.parse(localStorage.getItem('savedState'));
六、拓展——如何优雅的配置路由
这一篇文章讲了如何用传统的方式配置路由,但是往往传统的一大串路由看起来不够简洁,可读性也相比不高,今天给大家介绍于一种使用路由数组的方式进行路由的配置。
话不多说直接贴代码:
import { BrowserRouter, Navigate, useRoutes } from 'react-router'
import React from 'react'
// React.lazy 用于按需加载组件 -- 懒加载
const NoteClass = React.lazy(() => import('../pages/NoteClass'))
const Login = React.lazy(() => import('../pages/Login'))
// 路由列表
const routes = [
{
path: '/',
element: <Navigate to="/noteClass"/>,
},
{
path: '/login',
element: <Login/>,
},
// ...诸如此类路由对象
]
// useRoutes 是 react-router-dom v6.4+ 的新特性
// useRoutes 只能用在路由组件中,也就是说该组件不能被抛出,必须作为函数返回
function WrapperRoutes () {
// 将路由列表封装返回
return useRoutes(routes)
// 上述代码得到的是:
/*
<Routes>
<Route path="/" element={<Navigate to="/Component"/>}/>
<Route path="/login" element={<Login/>}/>
</Routes>
*/
}
// 再使用<BrowserRouter>将路由列表封装后抛出
export default function WrapperRouter () {
return (
<BrowserRouter>
<WrapperRoutes/>
</BrowserRouter>
)
// 上述代码得到的是:
/*
<BrowserRouter>
<Routes>
<Route path="/" element={<Navigate to="/Component"/>}/>
<Route path="/login" element={<Login/>}/>
</Routes>
</BrowserRouter>
*/
}
// 全局路由使用 App.jsx
import WrapperRouter from './router/index.jsx'
export default function App () {
return (
<>
<WrapperRouter/>
</>
)
}
代码的解释已经详细附在注释中,大家可以仔细阅读
七、总结
React 路由传参的三种核心方法各具特点:
- 查询字符串 适合公开、可分享的场景,但需注意安全性;
- 动态路由参数 是 RESTful 设计的首选,但灵活性较低;
- 状态对象 提供了更安全的私有数据传递方式,但不支持持久化。
在实际开发中,建议根据需求选择合适的方法:
- 对于公开资源(如商品详情页),优先使用动态路由参数;
- 对于临时数据(如登录后的用户信息),使用状态对象传参;
- 对于需要共享的查询条件(如搜索排序),使用查询字符串。
通过灵活组合这些方法,你可以构建出高效、安全的 React 单页应用路由体系。