【React】面试官:关于路由传参你知道哪些方法?

170 阅读5分钟

在 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/123 vs /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

实现方式

通过 navigatestate 选项传递参数,参数不会暴露在 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 单页应用路由体系。