React-路由监听 / 跳转 / 守卫全攻略(附实战代码)

前言

React Router 是 React 单页应用的核心路由库,除了基础的路由配置,日常开发中还会高频用到路由监听、编程式跳转、路由守卫等进阶功能。本文从实战角度拆解这三大核心能力,涵盖实现方式、场景对比、避坑要点,基于 React Router v6+ 版本(主流稳定版)讲解,新手也能快速落地!

一、 路由监听:如何捕捉 URL 的变化?

在 React 中,React 监听路由变化的本质是监听 URL 相关属性(pathname/search/params 等)的变化,触发自定义回调函数。以下是 3 种常用实现方式:

1. 核心方案:useLocation + useEffect

这是最通用的监听方式。通过 useLocation 获取当前路由完整信息(pathname/search/state 等),结合 useEffect 监听 location 对象变化,触发回调函数。

jsx 复制代码
import { useLocation } from 'react-router-dom';
import { useEffect } from 'react';

const App = () => {
  const location = useLocation();

  useEffect(() => {
    // 每次路由切换时执行
    console.log('当前路径:', location.pathname);
    console.log('搜索参数:', location.search);
  }, [location]); 
};

2. 精准监听:useParams 与 useSearchParams

如果你只关心某个特定的动态参数(如 id),直接监听参数对象会更高效。

  • useParams:监听动态路由参数(如 /user/:id 中的 id);

  • useSearchParams:监听 URL 搜索参数(如 ?page=1&size=10);

  • 适用场景:

    • 仅关注动态路由参数或搜索参数变化的场景(如详情页 ID 切换、列表页分页参数变化)。

3.监听原生路由事件(不推荐)

  • 通过 window.addEventListener 监听 popstate(History 模式)或 hashchange(Hash 模式)事件,直接捕获 URL 变化。

  • 缺点:React Router 已封装原生事件,手动监听易出现重复触发、状态不一致问题,仅建议特殊场景(如兼容老代码)使用。


二、 路由跳转

React Router 提供多种跳转方式,适配「点击跳转」「编程式跳转」「导航栏高亮」等不同场景:

1. 声明式导航:<Link> 与 <NavLink>(最常用)

  • <Link> :基础跳转,React Router 核心跳转组件,替代原生 <a> 标签(避免页面刷新),核心属性如下:

    • to:必传,目标路径(支持字符串 / 对象格式);

    • replace:默认 false(新增历史记录),true 则替换当前记录(跳转后无法回退);

    • state:传递自定义状态(不显示在 URL 中,通过 useLocation().state 获取)。

  • <NavLink> :专为导航栏设计,新增激活状态相关配置,适合导航栏场景:

    • isActive:可根据激活状态动态设置样式类名。

    • end:精准匹配模式,防止 / 匹配到所有子路由(如 /home 不匹配 /home/detail)。

2. 编程式导航:useNavigate

适用于点击按钮后的逻辑处理或异步请求后的跳转,通过 useNavigate Hook 获取导航函数,实现非点击触发的跳转(如接口请求后、条件判断后)。

jsx 复制代码
const navigate = useNavigate();

// 基础跳转,带状态
navigate('/profile', { replace: true, state: { from: 'home' } });

// 历史记录操作
navigate(-1); // 后退一步

三、 路由守卫:在 React 中如何"拦截"?

Vue 有原生的 beforeEach/afterEach 路由守卫,但 React Router 无专属 API,核心通过监听路由 + 条件判断实现,分为 3 类场景:

1. 全局路由守卫

实现逻辑

在路由根组件(如 App.jsx)中监听 location 变化,执行全局校验(如登录状态、白名单),拦截非法跳转。

实战代码

tsx 复制代码
import { useLocation, useNavigate, useEffect } from 'react-router-dom';
import { isLogin } from '@/utils/auth'; // 自定义登录校验函数

// 全局路由守卫组件
const GlobalRouterGuard = () => {
  const location = useLocation();
  const navigate = useNavigate();
  // 无需登录的白名单路由
  const whiteList = ['/login', '/register'];

  useEffect(() => {
    // 未登录且不在白名单 → 跳转到登录页
    if (!isLogin() && !whiteList.includes(location.pathname)) {
      navigate('/login', { 
        replace: true,
        state: { from: location.pathname } // 记录来源路径,登录后跳转回去
      });
    }
  }, [location.pathname, navigate]);

  return null; // 守卫组件无需渲染 DOM
};

// 在根路由中引入
// <BrowserRouter>
//   <GlobalRouterGuard />
//   <Routes>...</Routes>
// </BrowserRouter>

2. 组件内路由守卫

实现逻辑

在组件内通过 useEffect 实现进入守卫(组件挂载时校验),通过 useEffect 的返回函数实现离开守卫(组件卸载时执行)。

实战代码

tsx 复制代码
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { hasPermission } from '@/utils/permission'; // 自定义权限校验

const UserDetail = () => {
  const navigate = useNavigate();

  // 进入守卫:组件挂载时校验权限
  useEffect(() => {
    if (!hasPermission('user:view')) {
      navigate('/403', { replace: true });
    }
  }, [navigate]);

  // 离开守卫:组件卸载时执行(如保存表单、提示未提交内容)
  useEffect(() => {
    return () => {
      console.log('离开用户详情页,执行清理逻辑');
      // 业务逻辑:保存草稿、关闭WebSocket等
    };
  }, []);

  return <div>用户详情页</div>;
};

3. 路由独享守卫

  • 概念: 不影响全局其他路由,只针对这一个页面(或这一组页面)进行特殊的准入检查。 例如整个应用都可以访问,但只有/admin页面需要检查管理员权限

  • 实现 "先创建一个守卫组件AdminGuard,这个组件专门负责检查当前用户是不是管理员,然后将需要单独检查的Admin路由套在这个守卫组件AdminGuard里面

实战代码:

tsx 复制代码
// 守卫组件:AdminGuard.jsx
import { useNavigate } from 'react-router-dom';
import { isAdmin } from '@/utils/auth';

const AdminGuard = ({ children }) => {
  const navigate = useNavigate();
  
  // 管理员权限校验
  if (!isAdmin()) {
    navigate('/403', { replace: true });
    return null;
  }

  // 权限通过,渲染子组件
  return children;
};

// 路由配置中使用
import { Routes, Route } from 'react-router-dom';
import SettingsPage from '@/pages/Settings';

const RouterConfig = () => {
  return (
    <Routes>
      {/* 独享守卫:仅 /settings 路由触发管理员校验 */}
      <Route 
        path="/settings" 
        element={
          <AdminGuard>
            <SettingsPage />
          </AdminGuard>
        } 
      />
    </Routes>
  );
};

4.扩展:离开时的路由拦截(useBlocker)

useBlocker 是 React Router v6 新增 Hook,用于拦截所有路由跳转行为(包括 <Link>、navigate、浏览器前进 / 后退)。

tsx 复制代码
import { useBlocker } from 'react-router-dom';

// blockerFn:返回 true 拦截跳转,false 放行
// when:是否启用拦截(可选,默认 true)
useBlocker((tx) => {
  console.log('即将跳转到:', tx.location.pathname);
  return true; // 拦截跳转
}, when);

实战场景:表单未提交拦截

tsx 复制代码
import { useBlocker } from 'react-router-dom';

const FormPage = () => {
  const [formDirty, setFormDirty] = useState(false); // 表单是否修改

  // 表单未提交时拦截跳转
  useBlocker((tx) => {
    if (formDirty) {
      const confirm = window.confirm('表单内容未保存,是否确认离开?');
      return !confirm; // 点击取消 → 拦截(返回 true)
    }
    return false; // 放行
  }, formDirty); // 仅表单修改时启用拦截

  return (
    <form onChange={() => setFormDirty(true)}>
      <input type="text" placeholder="输入内容..." />
    </form>
  );
};

四、 总结与最佳实践

  1. 优先使用原生 Link :对于简单的跳转,<Link> 的性能和 SEO 优于 useNavigate。
  2. 善用 State 传参 :如果不想 URL 变得太长,利用 location.state 传递对象是最佳选择。
  3. 守卫逻辑模块化 :不要在 App.js 里写一堆 if-else,将权限校验封装成独立的 Guard 组件。
相关推荐
计算机魔术师3 小时前
从7月绕过沙盒到9月再曝违规,OpenAI的AI安全防线在漏什么
前端
我的div丢了肿么办3 小时前
element-plus表格使用:filter-method过滤导致出现无数据
前端·javascript·vue.js
F_Quant3 小时前
高股息+破净+PEG三因子:年化20%的背后,是一个“敢于证伪自己”的捡烟蒂策略
前端
帷幕落秋3 小时前
进程服务与文件描述符
linux·运维·面试
帷幕落秋3 小时前
信号、Ctrl 组合键与进程状态
linux·运维·面试
不可能片场3 小时前
Electron 发布核验 别把没查到当成没发出去
前端·electron
开开心心就好3 小时前
文件被占用删不掉?解锁工具强制删除
java·运维·服务器·开发语言·前端·pdf·ocr
inksci4 小时前
发布Ai生成的html markdown 推荐
前端·javascript·html
在西安放羊的牛油果4 小时前
WebP 图片优化方案详解
运维·前端·架构