React路由監(jiān)聽(tīng)/跳轉(zhuǎn)/守衛(wèi)的完整指南
前言
React Router 是 React 單頁(yè)應(yīng)用的核心路由庫(kù),除了基礎(chǔ)的路由配置,日常開(kāi)發(fā)中還會(huì)高頻用到路由監(jiān)聽(tīng)、編程式跳轉(zhuǎn)、路由守衛(wèi)等進(jìn)階功能。本文從實(shí)戰(zhàn)角度拆解這三大核心能力,涵蓋實(shí)現(xiàn)方式、場(chǎng)景對(duì)比、避坑要點(diǎn),基于 React Router v6+ 版本(主流穩(wěn)定版)講解,新手也能快速落地!
一、 路由監(jiān)聽(tīng):如何捕捉 URL 的變化?
在 React 中,React 監(jiān)聽(tīng)路由變化的本質(zhì)是監(jiān)聽(tīng) URL 相關(guān)屬性(pathname/search/params 等)的變化,觸發(fā)自定義回調(diào)函數(shù)。以下是 3 種常用實(shí)現(xiàn)方式:
1. 核心方案:useLocation+useEffect
這是最通用的監(jiān)聽(tīng)方式。通過(guò) useLocation 獲取當(dāng)前路由完整信息(pathname/search/state 等),結(jié)合 useEffect 監(jiān)聽(tīng) location 對(duì)象變化,觸發(fā)回調(diào)函數(shù)。
import { useLocation } from 'react-router-dom';
import { useEffect } from 'react';
const App = () => {
const location = useLocation();
useEffect(() => {
// 每次路由切換時(shí)執(zhí)行
console.log('當(dāng)前路徑:', location.pathname);
console.log('搜索參數(shù):', location.search);
}, [location]);
};
2. 精準(zhǔn)監(jiān)聽(tīng):useParams與useSearchParams
如果你只關(guān)心某個(gè)特定的動(dòng)態(tài)參數(shù)(如 id),直接監(jiān)聽(tīng)參數(shù)對(duì)象會(huì)更高效。
useParams:監(jiān)聽(tīng)動(dòng)態(tài)路由參數(shù)(如/user/:id中的id);useSearchParams:監(jiān)聽(tīng) URL 搜索參數(shù)(如?page=1&size=10);- 適用場(chǎng)景:
- 僅關(guān)注動(dòng)態(tài)路由參數(shù)或搜索參數(shù)變化的場(chǎng)景(如詳情頁(yè) ID 切換、列表頁(yè)分頁(yè)參數(shù)變化)。
3.監(jiān)聽(tīng)原生路由事件(不推薦)
- 通過(guò)
window.addEventListener監(jiān)聽(tīng)popstate(History 模式)或hashchange(Hash 模式)事件,直接捕獲 URL 變化。 - 缺點(diǎn):React Router 已封裝原生事件,手動(dòng)監(jiān)聽(tīng)易出現(xiàn)重復(fù)觸發(fā)、狀態(tài)不一致問(wèn)題,僅建議特殊場(chǎng)景(如兼容老代碼)使用。
二、 路由跳轉(zhuǎn)
React Router 提供多種跳轉(zhuǎn)方式,適配「點(diǎn)擊跳轉(zhuǎn)」「編程式跳轉(zhuǎn)」「導(dǎo)航欄高亮」等不同場(chǎng)景:
1. 聲明式導(dǎo)航:<Link>與<NavLink>(最常用)
<Link> :基礎(chǔ)跳轉(zhuǎn),React Router 核心跳轉(zhuǎn)組件,替代原生 <a> 標(biāo)簽(避免頁(yè)面刷新),核心屬性如下:
to:必傳,目標(biāo)路徑(支持字符串 / 對(duì)象格式);replace:默認(rèn)false(新增歷史記錄),true則替換當(dāng)前記錄(跳轉(zhuǎn)后無(wú)法回退);state:傳遞自定義狀態(tài)(不顯示在 URL 中,通過(guò)useLocation().state獲?。?。
<NavLink> :專為導(dǎo)航欄設(shè)計(jì),新增激活狀態(tài)相關(guān)配置,適合導(dǎo)航欄場(chǎng)景:
isActive:可根據(jù)激活狀態(tài)動(dòng)態(tài)設(shè)置樣式類名。end:精準(zhǔn)匹配模式,防止/匹配到所有子路由(如/home不匹配/home/detail)。
2. 編程式導(dǎo)航:useNavigate
適用于點(diǎn)擊按鈕后的邏輯處理或異步請(qǐng)求后的跳轉(zhuǎn),通過(guò) useNavigate Hook 獲取導(dǎo)航函數(shù),實(shí)現(xiàn)非點(diǎn)擊觸發(fā)的跳轉(zhuǎn)(如接口請(qǐng)求后、條件判斷后)。
const navigate = useNavigate();
// 基礎(chǔ)跳轉(zhuǎn),帶狀態(tài)
navigate('/profile', { replace: true, state: { from: 'home' } });
// 歷史記錄操作
navigate(-1); // 后退一步
三、 路由守衛(wèi):在 React 中如何“攔截”?
Vue 有原生的 beforeEach/afterEach 路由守衛(wèi),但 React Router 無(wú)專屬 API,核心通過(guò)監(jiān)聽(tīng)路由 + 條件判斷實(shí)現(xiàn),分為 3 類場(chǎng)景:
1. 全局路由守衛(wèi)
實(shí)現(xiàn)邏輯
在路由根組件(如 App.jsx)中監(jiān)聽(tīng) location 變化,執(zhí)行全局校驗(yàn)(如登錄狀態(tài)、白名單),攔截非法跳轉(zhuǎn)。
實(shí)戰(zhàn)代碼
import { useLocation, useNavigate, useEffect } from 'react-router-dom';
import { isLogin } from '@/utils/auth'; // 自定義登錄校驗(yàn)函數(shù)
// 全局路由守衛(wèi)組件
const GlobalRouterGuard = () => {
const location = useLocation();
const navigate = useNavigate();
// 無(wú)需登錄的白名單路由
const whiteList = ['/login', '/register'];
useEffect(() => {
// 未登錄且不在白名單 → 跳轉(zhuǎn)到登錄頁(yè)
if (!isLogin() && !whiteList.includes(location.pathname)) {
navigate('/login', {
replace: true,
state: { from: location.pathname } // 記錄來(lái)源路徑,登錄后跳轉(zhuǎn)回去
});
}
}, [location.pathname, navigate]);
return null; // 守衛(wèi)組件無(wú)需渲染 DOM
};
// 在根路由中引入
// <BrowserRouter>
// <GlobalRouterGuard />
// <Routes>...</Routes>
// </BrowserRouter>
2. 組件內(nèi)路由守衛(wèi)
實(shí)現(xiàn)邏輯
在組件內(nèi)通過(guò) useEffect 實(shí)現(xiàn)進(jìn)入守衛(wèi)(組件掛載時(shí)校驗(yàn)),通過(guò) useEffect 的返回函數(shù)實(shí)現(xiàn)離開(kāi)守衛(wèi)(組件卸載時(shí)執(zhí)行)。
實(shí)戰(zhàn)代碼
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { hasPermission } from '@/utils/permission'; // 自定義權(quán)限校驗(yàn)
const UserDetail = () => {
const navigate = useNavigate();
// 進(jìn)入守衛(wèi):組件掛載時(shí)校驗(yàn)權(quán)限
useEffect(() => {
if (!hasPermission('user:view')) {
navigate('/403', { replace: true });
}
}, [navigate]);
// 離開(kāi)守衛(wèi):組件卸載時(shí)執(zhí)行(如保存表單、提示未提交內(nèi)容)
useEffect(() => {
return () => {
console.log('離開(kāi)用戶詳情頁(yè),執(zhí)行清理邏輯');
// 業(yè)務(wù)邏輯:保存草稿、關(guān)閉WebSocket等
};
}, []);
return <div>用戶詳情頁(yè)</div>;
};
3. 路由獨(dú)享守衛(wèi)
- 概念: 不影響全局其他路由,只針對(duì)這一個(gè)頁(yè)面(或這一組頁(yè)面)進(jìn)行特殊的準(zhǔn)入檢查。 例如整個(gè)應(yīng)用都可以訪問(wèn),但只有
/admin頁(yè)面需要檢查管理員權(quán)限 - 實(shí)現(xiàn)”先創(chuàng)建一個(gè)守衛(wèi)組件
AdminGuard,這個(gè)組件專門(mén)負(fù)責(zé)檢查當(dāng)前用戶是不是管理員,然后將需要單獨(dú)檢查的Admin路由套在這個(gè)守衛(wèi)組件AdminGuard里面
實(shí)戰(zhàn)代碼:
// 守衛(wèi)組件:AdminGuard.jsx
import { useNavigate } from 'react-router-dom';
import { isAdmin } from '@/utils/auth';
const AdminGuard = ({ children }) => {
const navigate = useNavigate();
// 管理員權(quán)限校驗(yàn)
if (!isAdmin()) {
navigate('/403', { replace: true });
return null;
}
// 權(quán)限通過(guò),渲染子組件
return children;
};
// 路由配置中使用
import { Routes, Route } from 'react-router-dom';
import SettingsPage from '@/pages/Settings';
const RouterConfig = () => {
return (
<Routes>
{/* 獨(dú)享守衛(wèi):僅 /settings 路由觸發(fā)管理員校驗(yàn) */}
<Route
path="/settings"
element={
<AdminGuard>
<SettingsPage />
</AdminGuard>
}
/>
</Routes>
);
};
4.擴(kuò)展:離開(kāi)時(shí)的路由攔截(useBlocker)
useBlocker 是 React Router v6 新增 Hook,用于攔截所有路由跳轉(zhuǎn)行為(包括 <Link>、navigate、瀏覽器前進(jìn) / 后退)。
import { useBlocker } from 'react-router-dom';
// blockerFn:返回 true 攔截跳轉(zhuǎn),false 放行
// when:是否啟用攔截(可選,默認(rèn) true)
useBlocker((tx) => {
console.log('即將跳轉(zhuǎn)到:', tx.location.pathname);
return true; // 攔截跳轉(zhuǎn)
}, when);
實(shí)戰(zhàn)場(chǎng)景:表單未提交攔截
import { useBlocker } from 'react-router-dom';
const FormPage = () => {
const [formDirty, setFormDirty] = useState(false); // 表單是否修改
// 表單未提交時(shí)攔截跳轉(zhuǎn)
useBlocker((tx) => {
if (formDirty) {
const confirm = window.confirm('表單內(nèi)容未保存,是否確認(rèn)離開(kāi)?');
return !confirm; // 點(diǎn)擊取消 → 攔截(返回 true)
}
return false; // 放行
}, formDirty); // 僅表單修改時(shí)啟用攔截
return (
<form onChange={() => setFormDirty(true)}>
<input type="text" placeholder="輸入內(nèi)容..." />
</form>
);
};
四、 總結(jié)與最佳實(shí)踐
- 優(yōu)先使用原生 Link:對(duì)于簡(jiǎn)單的跳轉(zhuǎn),
<Link>的性能和 SEO 優(yōu)于useNavigate。 - 善用 State 傳參:如果不想 URL 變得太長(zhǎng),利用
location.state傳遞對(duì)象是最佳選擇。 - 守衛(wèi)邏輯模塊化:不要在
App.js里寫(xiě)一堆if-else,將權(quán)限校驗(yàn)封裝成獨(dú)立的 Guard 組件。
以上就是React路由監(jiān)聽(tīng)/跳轉(zhuǎn)/守衛(wèi)的完整指南的詳細(xì)內(nèi)容,更多關(guān)于React路由監(jiān)聽(tīng)/跳轉(zhuǎn)/守衛(wèi)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解在create-react-app使用less與antd按需加載
這篇文章主要介紹了詳解在create-react-app使用less與antd按需加載,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
如何在React中通過(guò)URL預(yù)覽Excel文件
這篇文章主要為大家詳細(xì)介紹了如何在React中通過(guò)URL預(yù)覽Excel文件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03
React在組件中如何監(jiān)聽(tīng)redux中state狀態(tài)的改變
這篇文章主要介紹了React在組件中如何監(jiān)聽(tīng)redux中state狀態(tài)的改變,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
詳解如何使用React Hooks請(qǐng)求數(shù)據(jù)并渲染
這篇文章主要介紹了如何使用React Hooks請(qǐng)求數(shù)據(jù)并渲染,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
React.InputHTMLAttributes實(shí)踐和注意事項(xiàng)
文章討論了如何在封裝組件中使用React.InputHTMLAttributes,以及如何通過(guò) {...inputProps} 動(dòng)態(tài)傳遞屬性,最后,文章總結(jié)了使用React.InputHTMLAttributes的最佳實(shí)踐和注意事項(xiàng),感興趣的朋友一起看看吧2024-11-11

