React Router(web) 全解析:知识点、工作注意点及面试重点

React Router 是 React 生态中最核心的路由解决方案,用于实现单页应用(SPA)的路由跳转、视图切换等功能。掌握其核心用法、工作中的避坑点及面试高频考点,是前端工程师的必备技能。本文将系统梳理相关内容,帮助你全面掌握 React Router。

一、核心知识点:从基础到进阶

React Router 经历了多个版本迭代,目前主流版本为 v6(较 v5 有较大改动,需重点关注)。以下是 v6 版本的核心知识点:

1.1 核心库与安装

React Router 针对不同环境提供了多个库,日常开发主要使用以下两个:

  • react-router-dom:用于浏览器环境(Web 开发核心库);

  • react-router-native:用于 React Native 移动开发。

安装命令(v6 版本):

javascript 复制代码
npm install react-router-dom@6 
yarn add react-router-dom@6

1.2 核心组件与作用

v6 版本精简了核心组件,取消了 v5 中的 <Switch><Redirect> 等,新增了 <Routes><Navigate> 等,核心组件及作用如下:

  • <BrowserRouter>:路由根容器,基于 HTML5 的 history API 实现路由(URL 无 # 号),需包裹整个应用;

  • <HashRouter>:基于 URL 的 hash 实现路由(URL 含 # 号),兼容低版本浏览器,部署简单;

  • <Routes>:替代 v5 的 <Switch>,用于包裹多个 <Route>,匹配到第一个符合条件的路由后停止匹配;

  • <Route>:定义路由规则,通过 path 属性指定路由路径,element 属性指定对应渲染组件;

  • <Link>:替代原生 <a> 标签,实现无刷新路由跳转,通过 to 属性指定目标路径;

  • <NavLink><Link> 的增强版,支持路由激活状态(可通过 className 配置激活样式);

  • <Navigate>:替代 v5 的 <Redirect>,实现路由重定向,通过 to 属性指定重定向目标;

  • <Outlet>:用于嵌套路由,在父路由组件中渲染子路由组件的占位符。

1.3 基础路由实现(v6)

最基础的路由配置示例,实现多页面无刷新跳转:

javascript 复制代码
// 入口文件 index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Router>
    <App />
  </Router>
);

// App.js
import { Routes, Route, Link } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Contact from './pages/Contact';

function App() {
  return (
    <div>
      {/* 导航栏 */}
      <nav>
        <Link to="/" style={{ marginRight: '10px' }}>首页</Link>
        <Link to="/about" style={{ marginRight: '10px' }}>关于我们</Link>
        <Link to="/contact">联系我们</Link>
      </nav>
      
      {/* 路由匹配区域 */}
      <Routes>
        <Route path="/" element={<Home />} /> {/* 首页路由 */}
        <Route path="/about" element={<About />} /> {/* 关于我们路由 */}
        <Route path="/contact" element={<Contact />} /> {/* 联系我们路由 */}
      </Routes>
    </div>
  );
}

1.4 进阶路由特性

1.4.1 动态路由与参数获取

用于匹配动态路径(如详情页),通过 :参数名 定义动态参数,使用 useParams() 钩子获取参数:

动态参数传参的适用场景和注意事项

适用场景:详情页(用户详情、商品详情等,需要通过唯一ID定位数据)

注意1:参数会显示在URL中,不适合传递敏感数据(如密码)

注意2:路由配置要写在更具体的路由后面,避免冲突(如先写/user/list,再写/user/:id)

javascript 复制代码
// 一、动态参数传参(补充完整:配置+传参+接收)
// 1. App.js 中配置动态路由:用 :参数名 定义占位符(这里是:id)
<Routes>
  // 动态路由:匹配 /user/101、/user/102 等路径
  <Route path="/user/:id" element={<UserDetail />} />
</Routes>

// 2. 发起动态参数传参(两种方式)
// 方式A:通过Link组件跳转传参(声明式导航)
// 在任意组件中使用Link,to属性直接拼接参数
import { Link } from 'react-router-dom';
function UserList() {
  return (
    <div>
      {/* 跳转到用户101的详情页,参数101拼在路径中 */}
      <Link to="/user/101">查看用户101详情</Link>
    </div>
  );
}

// 方式B:通过编程式导航传参(就是上面Login组件中用的navigate)
// navigate(`/user/${userInfo.id}`); // 拼接id参数

// 3. UserDetail.js 中获取动态参数
import { useParams } from 'react-router-dom';

function UserDetail() {
  // 调用useParams(),结构赋值取出id(参数名和路由配置的:id一致)
  const { id } = useParams(); 
  // 重要:useParams()获取的参数都是字符串类型!如果需要数字,要手动转:Number(id)
  return <div>
    <h3>用户详情页</h3>
    <p>用户ID:{id}(类型:{typeof id})</p>
    <p>用户ID(数字):{Number(id)}(类型:{typeof Number(id)})</p>
  </div>;
}

1.4.2 嵌套路由

实现路由的层级嵌套(如父页面包含子页面),通过 children 属性配置子路由,配合 <Outlet> 渲染子组件:

javascript 复制代码
// App.js 中配置嵌套路由
<Routes>
  <Route path="/dashboard" element={<Dashboard />} >
    <Route index element={<DashboardHome />} /> {/* 子路由默认页(index 表示默认匹配) */}
    <Route path="profile" element={<UserProfile />} /> {/* 子路由:/dashboard/profile */}
    <Route path="settings" element={<Settings />} /> {/* 子路由:/dashboard/settings */}
  </Route>
</Routes>

// Dashboard.js 中使用 Outlet 渲染子组件
import { Outlet, Link } from 'react-router-dom';

function Dashboard() {
  return (
    <div>
      <h1>控制台</h1>
      <nav>
        <Link to="profile" style={{ marginRight: '10px' }}>个人资料</Link>
        <Link to="settings">设置</Link>
      </nav>
      <Outlet /> {/* 子路由组件的占位符,渲染匹配的子组件 */}
    </div>
  );
}

1.4.3 编程式导航

通过 API 实现路由跳转(如按钮点击后跳转),使用 useNavigate() 钩子获取导航函数:

javascript 复制代码
// 1. 导入编程式导航所需的钩子:useNavigate
// 初学者注意:这个钩子只能在 React 函数组件内部使用
import { useNavigate } from 'react-router-dom';

// 登录组件(普通函数组件)
function Login() {
  // 2. 调用 useNavigate() 得到导航函数,命名为 navigate(名字可自定义)
  const navigate = useNavigate();

  // 3. 登录按钮点击事件处理函数(模拟登录逻辑)
  const handleLogin = () => {
    // 模拟登录验证:实际项目中这里会调用后端登录接口,验证账号密码
    const isLoginSuccess = true; // 假设登录成功(初学者可先固定为true测试)
    
    if (isLoginSuccess) {
      // 模拟后端返回的用户数据(含token和用户信息)
      const userInfo = {
        token: 'fake-token-123456',
        id: 101,
        name: '张三'
      };
      // 存储token(供受保护路由判断权限)
      localStorage.setItem('token', userInfo.token);
      
      // -------------- 路由传参详解(3种核心方式)--------------
      // 方式1:动态参数传参(适合传递简单标识,如ID,显示在URL中)
      // 适用场景:详情页(如用户详情、商品详情)
      navigate(`/user/${userInfo.id}`); // 跳转路径:/user/101
      
      // 方式2:state参数传参(隐式传参,不显示在URL中,适合传递复杂数据)
      // 适用场景:页面间传递敏感数据、复杂对象(如用户信息)
      // 注意:state参数刷新页面后会丢失,需持久化可存localStorage
      // navigate('/dashboard', {
      //   state: { user: userInfo } // 要传递的复杂数据
      // });
      
      // 方式3:查询参数传参(显示在URL中,格式:?key=value,适合传递可选参数)
      // 适用场景:列表页筛选、分页等(如?page=1&size=10)
      // navigate('/userList?page=1&size=10');
      
      // 补充:后退/前进页面(类似浏览器的前进后退按钮)
      // navigate(-1); // 后退1页
      // navigate(1); // 前进1页
    } else {
      // 登录失败的逻辑:比如提示"账号密码错误"
      alert("登录失败,请检查账号密码!");
    }
  };

  // 4. 渲染登录按钮,绑定点击事件
  return <button onClick={handleLogin}>登录</button>;
}

// ---------------- 对应传参方式的接收示例 ----------------
// 示例1:接收【动态参数】(对应方式1,以UserDetail组件为例)
import { useParams } from 'react-router-dom';
function UserDetail() {
  // 用useParams()获取动态参数,结构赋值取出id
  const { id } = useParams(); 
  // 注意:获取到的参数都是字符串类型,若需要数字需手动转换(如 Number(id))
  return <div>
    <p>用户详情页</p>
    <p>用户ID:{id}</p>
  </div>;
}

// 示例2:接收【state参数】(对应方式2,以Dashboard组件为例)
import { useLocation } from 'react-router-dom';
function Dashboard() {
  // 用useLocation()获取路由相关信息,其中state就是传递的参数
  const location = useLocation();
  const { user } = location.state || {}; // 加||{}避免未传参时报错
  
  return <div>
    <p>控制台</p>
    <p>欢迎您:{user?.name}(ID:{user?.id})</p> 
    <Outlet /> {/* 子路由占位符 */}
  </div>;
}

// 示例3:接收【查询参数】(对应方式3,以UserList组件为例)
import { useSearchParams } from 'react-router-dom';
function UserList() {
  // useSearchParams()返回数组:第一个是参数对象,第二个是修改参数的函数
  const [searchParams, setSearchParams] = useSearchParams();
  
  // 获取单个查询参数:get(参数名),返回字符串
  const page = searchParams.get('page') || 1; // 无参数时默认1
  const size = searchParams.get('size') || 10;
  
  // 修改查询参数(示例:切换到第2页)
  const goToPage2 = () => {
    setSearchParams({ page: 2, size: 10 }); // 会自动拼接成 ?page=2&size=10
  };
  
  return <div>
    <p>用户列表页 - 第{page}页,每页{size}条</p>
    <button onClick={goToPage2}>跳转到第2页</button>
  </div>;
}

1.4.4 路由守卫(权限控制)

初学者重点理解:路由守卫就是"路由的大门守卫",用来控制哪些人能访问某个路由。v6 版本取消了 v5 里复杂的配置方式,改用更简单的"受保护路由组件"实现,核心逻辑是:

  1. 先判断用户是否有权限(比如是否登录);

  2. 有权限就放行,显示目标页面;

  3. 没权限就拦住,强制跳转到登录页。

补充:路由守卫与路由传参的关联:登录后跳转时传递的用户信息(state参数),在受保护路由的目标组件(如Dashboard)中可以直接接收,无需重复请求接口,简化数据传递流程。

二、工作实际注意点

1.4.5 常用API详解

React Router v6 提供了多个实用的钩子函数(API),用于获取路由信息、实现导航、操作参数等,以下是开发中最常用的5个核心API,均支持在函数组件中直接使用:

1. useParams() ------ 获取动态路由参数

作用 :获取路由配置中定义的动态参数(如 /user/:id 中的 id),适用于详情页等需要通过唯一标识定位数据的场景。

语法const { 参数名1, 参数名2 } = useParams();

示例(延续用户详情页场景):

javascript 复制代码
import { useParams } from 'react-router-dom';

function UserDetail() {
  // 1. 结构赋值取出动态参数id(参数名必须和路由配置的:id一致)
  const { id } = useParams();
  
  // 2. 注意:获取到的参数是字符串类型,若需数字需手动转换
  const userId = Number(id);
  
  return <div>
    <h3>用户详情页</h3>
    <p>动态参数id(字符串):{id}</p>
    <p>转换后id(数字):{userId}</p>
  </div>;
}

注意事项 :参数名必须与 <Route path="/user/:id">: 后面的名称完全一致,否则无法获取;参数默认是字符串,数字类参数需手动转换。

2. useSearchParams() ------ 获取/修改查询参数

作用 :操作 URL 中 ? 后面的查询参数(如 /userList?page=1&size=10),支持获取参数和修改参数,适用于列表页筛选、分页等场景。

语法const [searchParams, setSearchParams] = useSearchParams();(类似 useState 的数组结构,第一个参数是参数对象,第二个是修改函数)

示例(延续用户列表分页场景):

javascript 复制代码
import { useSearchParams } from 'react-router-dom';

function UserList() {
  // 1. 获取查询参数对象和修改函数
  const [searchParams, setSearchParams] = useSearchParams();
  
  // 2. 获取单个参数:get(参数名),无参数时返回null,需设置默认值
  const page = searchParams.get('page') || 1; // 默认第1页
  const size = searchParams.get('size') || 10; // 默认每页10条
  
  // 3. 修改查询参数:调用setSearchParams,自动拼接URL
  const goToPage2 = () => {
    // 传对象:会覆盖原有查询参数(仅保留page和size)
    setSearchParams({ page: 2, size: 10 });
    // 若需保留原有参数,可先获取再修改:
    // setSearchParams(prev => {
    //   prev.set('page', 2);
    //   return prev;
    // });
  };
  
  return <div>
    <h3>用户列表页</h3>
    <p>当前页码:{page},每页条数:{size}</p>
    <button onClick={goToPage2}>跳转到第2页</button>
  </div>;
}

注意事项:get() 方法返回的是字符串,数字参数需手动转换;setSearchParams() 传对象会覆盖原有查询参数,保留原有参数需用回调函数修改。

3. useNavigate() ------ 编程式导航

作用:实现通过代码触发路由跳转(如按钮点击、登录成功后跳转),替代 v5 中的 useHistory(),功能更强大。

语法const navigate = useNavigate();(调用后返回导航函数)

核心用法

  • 基础跳转:navigate(目标路径)(如 navigate('/dashboard')

  • 带参数跳转:navigate(目标路径, { state: 数据 })(state参数隐式传递,不显示在URL)

  • 前进/后退:navigate(数字)(如 navigate(-1) 后退1页,navigate(1) 前进1页)

  • 替换历史记录:navigate(目标路径, { replace: true })(避免回退到当前页,适用于重定向)

示例(登录跳转场景):

javascript 复制代码
import { useNavigate } from 'react-router-dom';

function Login() {
  const navigate = useNavigate();

  const handleLogin = () => {
    const loginSuccess = true; // 模拟登录成功
    if (loginSuccess) {
      const userInfo = { name: '张三', id: 101 };
      // 1. 带state参数跳转,隐式传递用户信息
      navigate('/dashboard', {
        state: userInfo,
        replace: true // 替换历史记录,避免回退到登录页
      });
    }
  };

  return <button onClick={handleLogin}>登录</button>;
}

注意事项:该钩子只能在函数组件内部使用;state参数刷新页面后会丢失,敏感数据需配合localStorage持久化。

4. useLocation() ------ 获取路由位置信息

作用:获取当前路由的完整位置信息(如路径、搜索参数、state参数等),适用于需要获取当前路由状态的场景(如接收state参数、监听路由变化)。

语法const location = useLocation();

核心属性

  • location.pathname:当前路由路径(如 /dashboard

  • location.search:查询参数字符串(如 ?page=1&size=10

  • location.state:通过 navigate 传递的 state 参数(隐式参数)

  • location.hash:URL 中的 hash 值(如 #section1

示例(接收state参数场景):

javascript 复制代码
import { useLocation } from 'react-router-dom';
import { Outlet } from 'react-router-dom';

function Dashboard() {
  // 1. 获取路由位置信息
  const location = useLocation();
  
  // 2. 接收登录页传递的state参数(加||{}避免未传参时报错)
  const user = location.state || {};
  
  return <div>
    <h3>控制台</h3>
    <p>当前路径:{location.pathname}</p>
    <p>欢迎您:{user.name || '未登录用户'}</p>
    <Outlet /> {/* 子路由占位符 */}
  </div>;
}

注意事项 :location 对象会随着路由变化而更新,可配合 useEffect 监听路由变化(如 useEffect(() => { ... }, [location]))。

5. useRoutes() ------ 编程式配置路由

作用 :用 JavaScript 数组配置路由规则,替代 JSX 格式的 <Routes><Route>,适用于路由规则较复杂、需要动态生成路由的场景。

语法const element = useRoutes(路由规则数组);

示例(替代 App.js 中的 JSX 路由配置):

javascript 复制代码
import { useRoutes } from 'react-router-dom';
import Home from './pages/Home';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import UserDetail from './pages/UserDetail';
import ProtectedRoute from './components/ProtectedRoute';

function App() {
  // 1. 定义路由规则数组(和JSX配置一一对应)
  const routes = [
    { path: '/', element: <Home /> }, // 首页路由
    { path: '/login', element: <Login /> }, // 登录路由
    // 受保护路由组:element指定受保护组件,children是子路由
    {
      element: <ProtectedRoute />,
      children: [
        { path: '/dashboard', element: <Dashboard /> },
        { path: '/user/:id', element: <UserDetail /> }
      ]
    }
  ];
  
  // 2. 调用useRoutes生成路由组件
  const routeElement = useRoutes(routes);
  
  return (
    <div>
      {/* 导航栏(和之前一致) */}
      <nav>
        <Link to="/" style={{ marginRight: '10px' }}>首页</Link>
        <Link to="/login" style={{ marginRight: '10px' }}>登录</Link>
        <Link to="/dashboard">控制台</Link>
      </nav>
      {/* 渲染路由组件 */}
      {routeElement}
    </div>
  );
}

注意事项:路由规则数组的结构和 JSX 配置完全一致,只是用对象格式描述;动态路由、嵌套路由、受保护路由的配置逻辑和 JSX 方式相同,适合复杂项目统一管理路由规则。

在实际项目开发中,React Router 的使用需关注以下细节,避免踩坑:

1.4.6 RouterProvider(基于数据的路由)

RouterProvider 是 React Router v6.4+ 版本引入的核心组件,用于实现"基于数据的路由"(Data Router),替代了传统的<BrowserRouter>+<Routes> 组合。它通过 createBrowserRouter 等函数创建路由配置,支持路由加载数据(loader)、提交数据(action)、错误处理等高级功能,是 v6 后期推荐的路由配置方式。

核心作用

  • 统一管理路由规则:用数组配置所有路由,替代分散的 JSX 路由标签;

  • 内置高级功能:支持路由级别的数据加载/提交、错误边界、延迟加载等,无需额外配置;

  • 更灵活的路由控制:可动态修改路由配置,适配复杂项目需求。

核心用法(三步实现)

第一步:理解核心依赖与概念
  • createBrowserRouter:创建浏览器环境的路由实例,接收路由配置数组,返回可传递给 RouterProvider 的路由对象;

  • RouterProvider:路由根容器,接收 router 属性(即 createBrowserRouter 的返回值),替代传统的 <BrowserRouter>

  • 路由配置数组:每个元素是路由对象,包含 path(路由路径)、element(渲染组件)、children(子路由)等属性。

第二步:完整示例(替代传统路由配置)
javascript 复制代码
// 1. 导入核心依赖(v6.4+ 版本才支持,需确保安装对应版本)
App.tsx根节点:
import { RouterProvider } from 'react-router-dom'
import { router } from '@/router'
export default function App(): ReactElement {
  return (
    <Suspense fallback={<div style={{ padding: 16 }}></div>}>
        <RouterProvider router={router} />
    </Suspense>
  )
}
// 2. 定义路由配置数组(和 useRoutes 配置类似,支持嵌套、受保护路由)
index.tsx(router)
import { createBrowserRouter } from 'react-router-dom'
import { routes } from './routes'

routes.tsx
import { lazy } from 'react'
import type { RouteObject } from 'react-router-dom'
import AppLayout from '@/layouts/AppLayout'
//未登录不能访问的页面
import AuthGuard from './AuthGuard'
const Login = lazy(() => import('@/pages/Login/index'))
const NotFound = lazy(() => import('@/pages/NotFound'))
const WorkInfo = lazy(() => import('@/pages/Apply/WorkInfo'))
const ContactsInfo = lazy(() => import('@/pages/Apply/ContactsInfo'))
const PersonalInfo = lazy(() => import('@/pages/Apply/PersonalInfo'))
const IdInfo = lazy(() => import('@/pages/Apply/IdInfo'))
const FaceCapture = lazy(() => import('@/pages/Apply/FaceCapture'))
const BankInfo = lazy(() => import('@/pages/Apply/BankInfo'))

export const routes: RouteObject[] = [
  // 登录页
  { path: '/login', element: <Login /> },
  {
    // 路由守卫
    element: <AuthGuard />,
    children: [
      { path: 'work', element: <WorkInfo /> },
      { path: 'contacts', element: <ContactsInfo /> },
      { path: 'personal', element: <PersonalInfo /> },
      { path: 'id', element: <IdInfo /> },
      { path: 'face-capture', element: <FaceCapture /> },
      { path: 'bank', element: <BankInfo /> },
    ],
  },
  // 404 页面
  { path: '*', element: <NotFound /> },
]

AuthGuard.tsx 权限管理
import { type ReactElement } from 'react'
import { Navigate, Outlet } from 'react-router-dom'
import { getStorage, StorageKeys } from '@/utils/storage'

export default function AuthGuard(): ReactElement {
  // 检查登录状态
  const loginInfo = getStorage(StorageKeys.LOGIN_INFO)

  if (!loginInfo) {
    // 未登录跳转至登录页
    return <Navigate to="/login" replace />
  }

  // 已登录显示子路由
  return <Outlet />
}
第三步:注意事项
  • 版本要求:RouterProvider 是 v6.4+ 新增特性,需确保安装的 react-router-dom 版本 ≥ 6.4.0(可通过 npm list react-router-dom 查看版本);

  • 与传统路由的区别:RouterProvider 是"基于数据的路由"核心,替代了 <BrowserRouter>+<Routes>,但路由跳转(Link、useNavigate)、参数获取(useParams 等)API 完全兼容旧用法;

  • 高级功能入门:RouterProvider 支持 loader(路由进入前加载数据)、action(表单提交等操作)、errorElement(路由错误处理)等高级功能,初学者可先掌握基础配置,后续再深入;

  • 适用场景:小型项目用传统路由或 useRoutes 即可,中大型项目推荐用 RouterProvider,便于统一管理路由和数据逻辑。

补充:RouterProvider 与 useRoutes 的区别
  • 相同点:都支持用数组配置路由规则,核心配置项(path、element、children)一致;

  • 不同点:RouterProvider 是组件,需作为根容器包裹应用,支持 loader/action 等高级功能;useRoutes 是钩子函数,需在组件内部调用,返回路由组件,不支持高级数据功能。

2.1 路由模式选择(BrowserRouter vs HashRouter)

  • BrowserRouter(history 模式):URL 美观(无 # 号),但需要后端配置支持(解决刷新 404 问题)。部署时,后端需将所有路由请求转发到 index.html(如 Nginx 配置 try_files uri uri/ /index.html;);

  • HashRouter(hash 模式):URL 含 # 号,兼容性好,无需后端配置,适合静态页面部署(如 GitHub Pages)。但 # 号后的内容不会发送到服务器,可能影响 SEO。

注意:若项目需要 SEO 优化,优先选择 BrowserRouter,并配合后端配置;静态页面部署优先选择 HashRouter。

2.2 避免路由冲突

  • 路由匹配顺序:<Routes> 会按顺序匹配路由,需将更具体的路由放在前面(如 /user/:id 需放在 /user/list 后面,否则 /user/list 会被匹配为 /user/:id,id 为 list);

  • 使用 exact(v5)/ 嵌套路由(v6):v6 中 <Route> 默认是"精确匹配"的简化版,通过嵌套路由和 index 路由可避免冲突。

2.3 路由懒加载与代码分割

大型项目中,为优化首屏加载速度,需对路由组件进行懒加载(仅当访问该路由时才加载组件代码),配合 React 的 React.lazy()<Suspense> 实现:

javascript 复制代码
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

// 懒加载路由组件(动态导入)
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

function App() {
  return (
    <Suspense fallback={<div>加载中...</div>} > {/* 加载过程中显示的占位内容 */}
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/Suspense&gt;
  );
}

2.4 路由参数与状态管理

  • 路由传参方式:

    • 动态参数(/user/:id):适合传递简单标识(如 ID),参数会显示在 URL 中;

    • state 参数(navigate('/user', { state: { name: '张三' } })):适合传递复杂数据,参数不会显示在 URL 中,刷新页面后会丢失(需配合本地存储持久化);

    • 查询参数(/user?name=张三):适合传递可选参数,使用 useSearchParams() 钩子获取和修改。

  • 避免路由参数过度使用:复杂状态(如用户信息、全局配置)建议使用 Redux、Context 等状态管理工具,而非路由参数。

2.5 404 页面配置

通过 path="*" 匹配所有未定义的路由,实现 404 页面:

javascript 复制代码
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="*" element={<NotFound />} /> {/* 404 页面,匹配所有未定义的路由 */}
</Routes>

三、面试重点:高频问题与核心答案

React Router 是前端面试的高频考点,以下是常见问题及精准回答思路:

3.1 问题 1:React Router 的核心原理是什么?

核心答案:

  • React Router 基于"单页应用(SPA)"的核心思想,通过监听 URL 的变化,在不刷新页面的前提下,动态切换渲染对应的组件;

  • 底层依赖两种 URL 变化监听方式:

    • history 模式(BrowserRouter):基于 HTML5 的 history.pushState()history.replaceState() API,可修改 URL 而不触发页面刷新,通过监听 popstate 事件处理浏览器前进/后退;

    • hash 模式(HashRouter):基于 URL 的 hash(# 号后的内容),通过监听 hashchange 事件感知 hash 变化,进而切换组件。

3.2 问题 2:React Router v5 和 v6 有哪些主要区别?

核心答案(重点区别):

  • 路由容器组件:v5 使用 <Switch> 匹配路由,v6 替换为 <Routes>,匹配逻辑更高效(匹配到第一个即停止);

  • Route 组件属性:v5 使用 componentrender 属性指定组件,v6 统一使用 element 属性(需传递组件实例,如 element={<Home />});

  • 嵌套路由:v5 需手动拼接父路由路径,v6 支持嵌套路由配置(children 属性),配合 <Outlet> 渲染子组件,更简洁;

  • 编程式导航:v5 使用 useHistory() 钩子,v6 替换为 useNavigate() 钩子,功能更强大(支持前进/后退、替换历史记录等);

  • 重定向:v5 使用 <Redirect> 组件,v6 替换为 <Navigate> 组件,通过 replace 属性实现重定向。

3.3 问题 3:如何实现 React Router 的权限控制(路由守卫)?

核心答案(v6 方案):

  • 实现思路:创建"受保护路由组件",在组件内部判断用户权限(如是否登录、是否有角色权限),根据权限决定渲染子组件或重定向到登录页;

  • 核心实现:利用 <Outlet> 组件渲染子路由,未授权时使用 <Navigate> 重定向;

  • 扩展:可根据角色实现更精细的权限控制(如管理员才能访问 /admin 路由),在受保护组件中添加角色判断逻辑。

3.4 问题 4:history 模式和 hash 模式的区别,以及 history 模式部署时的注意事项?

核心答案:

  • 区别:

    • URL 外观:history 模式无 # 号,hash 模式有 # 号;

    • 底层原理:history 模式基于 HTML5 history API,hash 模式基于 hashchange 事件;

    • 后端依赖:history 模式需要后端配置,hash 模式无需后端配置;

    • SEO 影响:history 模式更利于 SEO(URL 更规范),hash 模式 # 号后内容不被搜索引擎抓取。

  • history 模式部署注意事项:后端需将所有路由请求转发到 index.html(如 Nginx 配置 try_files uri uri/ /index.html;),否则刷新页面会出现 404 错误(因为浏览器会将路由路径当作真实接口请求后端)。

3.5 问题 5:如何实现路由懒加载?

核心答案:

  • React Router 结合 React 内置的 React.lazy()<Suspense> 实现路由懒加载;

  • 核心逻辑:React.lazy() 接收一个动态导入函数(() => import('./pages/Home')),返回一个懒加载组件;<Suspense> 组件用于包裹懒加载组件,指定加载过程中的占位内容(fallback 属性);

  • 注意:懒加载组件只能用于 <Suspense> 内部,否则会报错。

3.6 问题 6:useParams、useSearchParams、useNavigate 各自的作用?

核心答案:

  • useParams():用于获取动态路由参数(如 /user/:id 中的 id),返回一个包含参数的对象;

  • useSearchParams():用于获取和修改 URL 中的查询参数(如 /user?name=张三),返回一个数组,第一个元素是 SearchParams 对象(可通过 get() 方法获取参数),第二个元素是修改查询参数的函数;

  • useNavigate():用于实现编程式导航,返回一个导航函数,可通过传递路径和配置对象实现跳转、重定向、前进/后退等功能。

四、总结

React Router 是 React 单页应用的核心路由解决方案,需重点掌握 v6 版本的核心组件(<Routes><Route><Outlet> 等)、动态路由、嵌套路由、编程式导航、权限控制等核心功能。工作中需注意路由模式选择、路由冲突避免、懒加载优化、404 页面配置等细节;面试中则需深入理解其实现原理、v5 与 v6 的区别、权限控制实现方式等核心考点。

建议结合实际项目多练习,尤其是嵌套路由和权限控制场景,加深对 React Router 用法和原理的理解。

相关推荐
EQ_雪梨蛋花汤2 小时前
【NDK / JNI】Sceneform-EQR 集成 Filament JNI 源码:关键点与逐步操作记录
前端
C_心欲无痕2 小时前
vue3 - shallowReactive浅层响应式对象(只对顶层属性)
前端·javascript·vue.js
AY呀2 小时前
新手必读:React组件从入门到精通,一篇文章搞定所有核心概念
前端·javascript·react.js
葛葵葵2 小时前
使用 AI Workflow 规范化团队 Commit 信息:从混乱到有序
前端
Maxkim2 小时前
「✍️JS原子笔记 」一文搞懂 call、apply、bind 特征及手写实现
前端·javascript·面试
iccb10132 小时前
客服系统前端主题配色动态切换的一种实现思路(含代码)
前端
karshey2 小时前
【前端】svelte支持scss,包管理器是webpack
前端·webpack·scss
Можно2 小时前
深入理解 HTML 中的 iframe:特性、用法与现代实践
前端·html
布局呆星2 小时前
Vue 3 事件处理与列表渲染---02
前端·javascript·vue.js