React路由拦截器详解

在React中,路由拦截器是一种机制,用于在导航到特定路由之前执行一些逻辑,比如权限校验、用户认证或动态路由控制。通常,React使用react-router-dom库来管理路由,通过<Routes><Route>定义路由规则。

实现路由拦截的常见方式包括以下几种:


1. 使用<Navigate>实现重定向拦截

通过react-router-dom<Navigate>组件,可以在用户未通过权限校验时将其重定向到指定页面。

示例代码

import React from "react";
import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";// 模拟认证状态
const isAuthenticated = false;// 路由守卫组件
const ProtectedRoute = ({ children }) => {return isAuthenticated ? children : <Navigate to="/login" />;
};const App = () => {return (<Router><Routes><Route path="/login" element={<Login />} /><Routepath="/dashboard"element={<ProtectedRoute><Dashboard /></ProtectedRoute>}/></Routes></Router>);
};const Login = () => <h2>登录页面</h2>;
const Dashboard = () => <h2>仪表板页面</h2>;export default App;

2. 使用useEffect在组件中拦截导航

可以在页面组件中使用useEffect处理路由进入的逻辑,比如权限检查或数据初始化。

示例代码

import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";const Dashboard = () => {const navigate = useNavigate();useEffect(() => {// 模拟权限检查const hasAccess = false;if (!hasAccess) {navigate("/login");}}, [navigate]);return <h2>仪表板页面</h2>;
};export default Dashboard;

3. 使用react-router的嵌套路由和Layout组件

通过在布局组件中统一处理路由拦截,可以实现更简洁的权限管理。

示例代码

import React from "react";
import { BrowserRouter as Router, Routes, Route, Navigate, Outlet } from "react-router-dom";// 模拟认证状态
const isAuthenticated = false;// 布局组件
const ProtectedLayout = () => {return isAuthenticated ? <Outlet /> : <Navigate to="/login" />;
};const App = () => {return (<Router><Routes><Route path="/login" element={<Login />} /><Route element={<ProtectedLayout />}><Route path="/dashboard" element={<Dashboard />} /><Route path="/settings" element={<Settings />} /></Route></Routes></Router>);
};const Login = () => <h2>登录页面</h2>;
const Dashboard = () => <h2>仪表板页面</h2>;
const Settings = () => <h2>设置页面</h2>;export default App;

4. 中间件逻辑封装(高阶组件)

如果需要复用逻辑,可以封装为高阶组件(HOC)。

示例代码

import React from "react";
import { Navigate } from "react-router-dom";const withAuth = (Component) => {return (props) => {const isAuthenticated = false; // 模拟认证状态return isAuthenticated ? <Component {...props} /> : <Navigate to="/login" />;};
};const Dashboard = () => <h2>仪表板页面</h2>;export default withAuth(Dashboard);

总结

  • 小型项目:可以直接在组件内使用useNavigate<Navigate>处理路由跳转。
  • 中型项目:推荐使用布局组件(如<Outlet>)统一管理路由拦截。
  • 大型项目:封装高阶组件(HOC)或自定义Hook,结合reduxcontext进行权限状态管理。

根据具体需求选择合适的实现方式!

案例:

import React, { useCallback, useEffect, useMemo, useState } from "react";
import { getMenuParentKey } from "@/utils";
import { useDidRecover } from "react-router-cache-route"
import Error from "@pages/err";
import { Spin } from "antd";
import { useLocation } from "react-router-dom";
import { useDispatchLayout, useDispatchMenu, } from "@/store/hooks";const scrollPage = () => {window.scrollTo({top: 0,left: 0,behavior: "smooth",});
}const fallback = <Spin style={{display: "flex",alignItems: "center",justifyContent: "center",minHeight: 500,fontSize: 24,
}} tip="页面加载中...." />function Intercept({ menuList, components: Components, [MENU_TITLE]: title, [MENU_PATH]: pagePath, [MENU_KEEPALIVE]: isKeep, pageKey, [MENU_LAYOUT]: layout, ...itemProps }) {const location = useLocation()const { stateAddOpenedMenu: addOpenedMenuFn, stateSetSelectMenuKey: setSelectedKeys, stateSetOpenMenuKey: setOpenKeys, stateSetCurrentPath: setPath } = useDispatchMenu()const { stateChangeLayout } = useDispatchLayout()const [pageInit, setPageInit] = useState(false)const currentPath = useMemo(() => {const { pathname, search } = locationreturn pathname + search}, [location])// 监听 location 改变const onPathChange = useCallback(() => {if (isKeep !== "true") {addOpenedMenuFn({ key: currentPath, path: currentPath, title: title || "未设置标题信息" });}}, [currentPath, title, isKeep, addOpenedMenuFn])const setCurrentPageInfo = useCallback(() => {if (!title) {return;}document.title = title;setSelectedKeys([String(pageKey)]);let openkey = getMenuParentKey(menuList, pageKey);setOpenKeys(openkey);addOpenedMenuFn({ key: currentPath, path: currentPath, title });}, [currentPath, menuList, title, pageKey, setOpenKeys, setSelectedKeys, addOpenedMenuFn])const init = useCallback(() => {setCurrentPageInfo()scrollPage()}, [setCurrentPageInfo])useEffect(() => {if (!pageInit) {init()setPageInit(true)}}, [init, pageInit])useEffect(() => {if (pageInit) {onPathChange()}}, [onPathChange, pageInit])// 切换布局useEffect(() => {layout && stateChangeLayout("push", layout)}, [layout, stateChangeLayout])// 路由改变useEffect(() => {setPath(currentPath)}, [currentPath, setPath])useDidRecover(() => {setPath(currentPath)init()}, [init, currentPath, setPath])const hasPath = !menuList.find((m) => (m[MENU_PARENTPATH] || "") + m[MENU_PATH] === pagePath);if (hasPath && pagePath !== "/" && pagePath !== "*") {return (<Error{...itemProps}status="403"errTitle="权限不够"subTitle="Sorry, you are not authorized to access this page."/>);}return (<Components{...itemProps}fallback={fallback}/>);
}
export default Intercept

这段代码定义了一个名为Intercept的React组件,核心作用是作为一个路由拦截器,完成以下任务:

  1. 初始化页面状态和路径管理

    • 设置页面标题、打开菜单、选中菜单、当前路径等信息。
    • 通过useEffectuseCallback处理组件的生命周期及路径变化。
  2. 权限检查

    • 检查当前路径是否有对应的菜单项。如果没有权限访问当前路径,返回一个错误组件,显示403页面。
  3. 页面布局切换

    • 动态切换页面布局(layout)。
  4. 路由恢复逻辑

    • 监听路由的切换和恢复事件(useDidRecover),以确保页面状态在路径切换后能够正确恢复。

核心功能分析

1. 页面初始化逻辑
  • 变量初始化

    const currentPath = useMemo(() => {const { pathname, search } = location;return pathname + search;
    }, [location]);
    
    • 使用useMemo监听location变化,动态计算当前完整路径。
  • 初次加载初始化

    useEffect(() => {if (!pageInit) {init();setPageInit(true);}
    }, [init, pageInit]);
    
    • 在组件首次渲染时,调用init函数完成页面初始化工作(如设置标题、菜单状态等)。
  • 路径变化监听

    useEffect(() => {if (pageInit) {onPathChange();}
    }, [onPathChange, pageInit]);
    
    • 当路径发生变化时,执行onPathChange更新已打开的菜单列表。
2. 权限检查
  • 核心逻辑
    const hasPath = !menuList.find((m) => (m[MENU_PARENTPATH] || "") + m[MENU_PATH] === pagePath
    );if (hasPath && pagePath !== "/" && pagePath !== "*") {return (<Error{...itemProps}status="403"errTitle="权限不够"subTitle="Sorry, you are not authorized to access this page."/>);
    }
    
    • 检查menuList中是否有匹配的路径:
      • 如果路径不合法(即未在menuList中找到对应项),则渲染403错误页面。
      • 排除根路径"/"和通配路径"*"
3. 动态布局切换
  • 布局切换逻辑
    useEffect(() => {layout && stateChangeLayout("push", layout);
    }, [layout, stateChangeLayout]);
    
    • 如果传入了layout属性,则调用stateChangeLayout方法切换布局模式。
4. 路由恢复支持
  • 恢复逻辑
    useDidRecover(() => {setPath(currentPath);init();
    }, [init, currentPath, setPath]);
    
    • 当路由恢复时,重新初始化页面状态并设置当前路径。

代码结构总结

1. 核心流程
  • 初始化阶段:通过init设置页面标题、菜单状态、路径信息,并首次渲染组件内容。
  • 路径变化监听:在useEffect中根据location变化更新状态。
  • 权限检查:验证路径是否存在于menuList,否则渲染403页面。
  • 布局切换:根据layout动态改变布局。
2. 关键点拆解
  • 性能优化
    • 使用useMemouseCallback优化重复计算和函数重建。
  • 状态管理
    • 集成了多个状态管理方法(如useDispatchMenuuseDispatchLayout)。
  • 用户体验
    • 自动恢复页面状态,确保路径变化后体验一致。
3. 代码模块化
  • 权限校验:独立实现逻辑,返回错误页面。
  • 状态管理:通过useDispatchsetState组合处理菜单、路径和布局等全局状态。

改进建议

  1. 增强代码可读性

    • 把复杂逻辑(如路径变化监听、权限检查)拆分成独立的工具函数,提高代码复用性和可维护性。
    • 添加注释解释每个核心逻辑。
  2. 减少依赖传递

    • 使用Context或Redux集中管理状态,减少从props中传递过多参数。
  3. 错误处理和提示优化

    • menuList为空或useDispatchMenu未初始化的情况进行额外的边界处理。
  4. 性能监控

    • 在路径变化频繁时,注意useEffect中逻辑是否存在性能瓶颈(如大量DOM操作或API调用)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66488.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣经典题目之219. 存在重复元素 II

今天继续给大家分享一道力扣的做题心得今天这道题目是 219. 存在重复元素 II&#xff0c;我使用 hashmap 的方法来解题 题目如下&#xff0c;题目链接&#xff1a;219. 存在重复元素 II 1&#xff0c;题目分析 此题目给我们了一个整数数组 nums 和一个整数 k &#xff0c;需要…

四、VSCODE 使用GIT插件

VSCODE 使用GIT插件 一下载git插件与git Graph插件二、git插件使用三、文件提交到远程仓库四、git Graph插件 一下载git插件与git Graph插件 二、git插件使用 git插件一般VSCode自带了git&#xff0c;就是左边栏目的图标 在下载git软件后vscode的git插件会自动识别当前项目 …

消息队列MQ(二)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 MQ学习笔记 前言一、发送者的可靠性1. 生产者重试机制2. 生产者确认机制3. 实现生产者确认 二、MQ的可靠性1. 数据持久化2. LazyQueue 前言 在用MQ实现异步调用时&#xff0…

docker 常用命令实践DEMO

1.1 docker run -d -p 8080:80 --name web_server nginx 命令的详细解读 docker run: 这是 Docker 的一个基本命令&#xff0c;用于从指定的镜像启动一个新的容器。 -d: 这个参数是 --detach 的简写&#xff0c;意味着容器将在后台运行。也就是说&#xff0c;命令会立即返回&a…

Ubuntu18.04离线安装audit

Ubuntu18.04离线安装audit 查看ubuntu系统版本 lsb_release -a安装版本 下载地址 https://launchpad.net/ubuntu/bionic/arm64/libauparse0/1:2.8.2-1ubuntu1.1 https://launchpad.net/ubuntu/bionic/arm64/auditd/1:2.8.2-1ubuntu1 sudo dpkg -i libauparse0_2.8.2-1ubunt…

Meilisearch ASP.Net Core API 功能demo

安装 MeiliSearch 0.15.5 0.15.5demo code using Meilisearch; using System.Data; using System.Text.Json; using System.Text.Json.Serialization;namespace MeiliSearchAPI {public class MeilisearchHelper{public MeilisearchHelper(){DefaultClient…

关于element自定义样式popper-class

当我们在使用element组件时&#xff0c;会遇到需要修改组件的样式&#xff0c;但是样式无法覆盖原样式的情况。 用popper-class属性&#xff0c;给组件传递样式&#xff0c; 原理&#xff1a;其实就是传递给组件一个class名&#xff0c;然后设置class的样式&#xff0c;所以自定…

2024.1.5总结

今日不开心:这周本来想花点时间学习的&#xff0c;没想到全都花在刷视频&#xff0c;外出消费去了。 今日思考: 1.找对象这件事确实不能强求&#xff0c;顺其自然吧&#xff0c;单身和不单身&#xff0c;其实&#xff0c;各有各的利弊。在一次坐地铁的过程中&#xff0c;我一…

数据分析思维(九):分析方法——AARRR模型分析方法

数据分析并非只是简单的数据分析工具三板斧——Excel、SQL、Python&#xff0c;更重要的是数据分析思维。没有数据分析思维和业务知识&#xff0c;就算拿到一堆数据&#xff0c;也不知道如何下手。 推荐书本《数据分析思维——分析方法和业务知识》&#xff0c;本文内容就是提取…

【计算机网络】课程 实验四 配置快速生成树协议(RSTP)

实验四 配置快速生成树协议&#xff08;RSTP&#xff09; 一、实验目的 1&#xff0e;理解快速生成树协议RSTP的工作原理。 2&#xff0e;掌握如何在交换机上配置快速生成树。 二、实验分析与设计 【背景描述】 某学校为了开展计算机教学和网络办公&#xff0c;建立了一个计…

Tauri教程-基础篇-第一节 Tauri项目创建及结构说明

“如果结果不如你所愿&#xff0c;就在尘埃落定前奋力一搏。”——《夏目友人帐》 “有些事不是看到了希望才去坚持&#xff0c;而是因为坚持才会看到希望。”——《十宗罪》 “维持现状意味着空耗你的努力和生命。”——纪伯伦 Tauri 技术教程 * 第四章 Tauri的基础教程 第一节…

【Docker项目实战】使用Docker部署Typemill轻量级平面文件CMS

【Docker项目实战】使用Docker部署Typemill轻量级平面文件CMS 一、Typemill介绍1.1 Typemill简介1.2 主要特点1.3 主要使用场景二、本次实践规划2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本四、下载…

pyinstaller冻结打包多进程程序的bug:无限创建进程直至系统崩溃

前面写过两篇相关的文章&#xff1a; PyQt应用程序打包Python自动按键 这两篇文章都没有提到下面的这个重要问题&#xff1a; 采用Pyinstaller冻结打包多进程程序时&#xff0c;必须非常小心。这个技术线在Windows上会有一个非常严重的Bug。直接运行打包后的程序会造成无限创…

网络安全-kail linux 网络配置(基础篇)

一、网络配置 1.查看网络IP地址&#xff0c; 我的kail&#xff1a;192.168.15.128 使用ifconfig查看kail网络连接情况&#xff0c;ip地址情况 又复制了一台kail计算机的IP地址。 再看一下windows本机&#xff1a;使用ipconfig进行查看&#xff1a; 再看一下虚拟机上的win7I…

window.open 被浏览器拦截解决方案

前言 在项目开发中&#xff0c;点击支付按钮后需要发送支付请求&#xff0c;并在请求完成后的回调中&#xff0c;经过一系列判断&#xff0c;符合某种条件下弹出一个新窗口页面。自然想到使用 window.open&#xff0c;但发现该操作会被浏览器拦截。 分析原因 当浏览器检测到…

uni app 写的 小游戏,文字拼图?文字拼写?不知道叫啥

从下方的偏旁部首中选在1--3个组成上面文章中的文字&#xff0c;完成的文字标红 不喜勿喷 《满江红》 其中用到了两个文件 strdata.json parameters.json 这两个文件太大 放到资源中了 资源文件 <template><view class"wenzi_page_main"><view c…

qt编译环境异常问题解决一例

编译程序提示错误信息&#xff1a; dependent ..\..\..\..\..\..\src\vcpkg\installed\x64-windows\include\qt5\QtCore\QRunnable does not exist 具体找不到哪个类就看你的代码具体要include哪个头文件是第一个&#xff0c;也有可能是提示找不到QMainWindow&#xff0c;总…

分享几个高清无水印国外视频素材网站

在数字内容创作日益盛行的今天&#xff0c;高质量的视频素材成为了视频制作、广告创意和多媒体项目中不可或缺的元素。对于追求专业水准的创作者而言&#xff0c;高清、无水印的视频素材是确保作品质量的基石。以下将分享几个优质的视频素材网站&#xff0c;为您的创作之路提供…

【LLM】大语言模型基础知识及主要类别架构

文章目录 LLM大语言模型1.LLM基础知识1.1大模型介绍:1.2语言模型1.21n-gram语言模型1.22神经网络语言模型1.23基于Transformer的预训练语言模型1.24大语言模型 1.3模型评估指标1.31 BLEU1.32 Rouge指标1.33 困惑度PPL 2.LLM主要类别架构2.1 自编码模型2.2 自回归模型2.3 Encode…

剖析 Claim-Check 模式:以小传大,赋能分布式系统与微服务

1. 前言 1.1 写作背景与目的 在当今分布式系统与微服务架构盛行的时代&#xff0c;服务间的消息传递与数据交换越来越频繁。传统的消息传输在面对海量数据时&#xff0c;往往会遇到以下痛点&#xff1a; 消息体过大&#xff1a;直接通过消息队列或服务间接口发送大体量数据&…