使用React-Router實(shí)現(xiàn)前端路由鑒權(quán)的示例代碼
React-Router
是React生態(tài)里面很重要的一環(huán),現(xiàn)在React的單頁(yè)應(yīng)用的路由基本都是前端自己管理的,而不像以前是后端路由,React管理路由的庫(kù)常用的就是就是 React-Router
。本文想寫一下 React-Router
的使用,但是光介紹API又太平淡了, 而且官方文檔已經(jīng)寫得很好了 ,我這里就用一個(gè)常見(jiàn)的開發(fā)場(chǎng)景來(lái)看看 React-Router
是怎么用的吧。而我們一般的系統(tǒng)都會(huì)有用戶訪問(wèn)權(quán)限的限制,某些頁(yè)面可能需要用戶具有一定的權(quán)限才能訪問(wèn)。本文就是用 React-Router
來(lái)實(shí)現(xiàn)一個(gè)前端鑒權(quán)模型。
本文全部代碼已經(jīng)上傳GitHub,大家可以拿下來(lái)玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-router-usage
應(yīng)用示例
本文要實(shí)現(xiàn)的功能是大家經(jīng)常遇到的場(chǎng)景,就是要控制不同的用戶角色來(lái)訪問(wèn)不同的頁(yè)面,這里總共有四個(gè)頁(yè)面:
- /index: 網(wǎng)站首頁(yè)
- /login: 登錄頁(yè)
- /backend:后臺(tái)頁(yè)面
- /admin:管理頁(yè)面
另外還有三種角色:
- 未登錄用戶:只能訪問(wèn)網(wǎng)站首頁(yè)/index和登錄頁(yè)/login
- 普通用戶:可以訪問(wèn)網(wǎng)站首頁(yè)/index,登錄頁(yè)/login和后臺(tái)頁(yè)面/backend
- 管理員:可以訪問(wèn)管理頁(yè)面/admin和其他所有頁(yè)面
引入React-Router
要實(shí)現(xiàn)路由鑒權(quán),我們還得一步一步來(lái),我們先用React-Router搭建一個(gè)簡(jiǎn)單的帶有這幾個(gè)頁(yè)面的項(xiàng)目。我們直接用 create-react-app
創(chuàng)建一個(gè)新項(xiàng)目,然后建了一個(gè) pages
文件夾,里面放入我們前面說(shuō)的那幾個(gè)頁(yè)面:
我們頁(yè)面先寫簡(jiǎn)單點(diǎn),先寫個(gè)標(biāo)題吧,比如這樣:
import React from 'react'; function Admin() { return ( <h1>管理員頁(yè)面</h1> ); }
其他幾個(gè)頁(yè)面也是類似的。
然后我們就可以在 App.js
里面引入 React-Router
做路由跳轉(zhuǎn)了,注意我們?cè)跒g覽器上使用的是 react-router-dom
,新版的 React-Router
將核心邏輯層和展示層分開了,核心邏輯會(huì)處理路由匹配等,展示層會(huì)處理實(shí)際的跳轉(zhuǎn)和路由變化的監(jiān)聽(tīng),之所以這么分,是因?yàn)镽eact-Router不僅僅需要支持瀏覽器,還需要支持React Native,這兩個(gè)平臺(tái)的監(jiān)聽(tīng)和跳轉(zhuǎn)是不一樣的,所以現(xiàn)在 React-Router 下面有好幾個(gè)包了:
react-router
:核心邏輯處理,提供一些公用的基類react-router-dom
:具體實(shí)現(xiàn)瀏覽器相關(guān)的路由監(jiān)聽(tīng)和跳轉(zhuǎn)react-router-native
:具體實(shí)現(xiàn)RN相關(guān)的路由監(jiān)聽(tīng)和跳轉(zhuǎn)
在實(shí)際使用時(shí),我們一般不需要引用 react-router
,而是直接用 react-router-dom
就行,因?yàn)樗约簳?huì)去引用 react-router
。下面我們?cè)陧?xiàng)目里面引入 react-router-dom
。
import React from 'react'; import { BrowserRouter as Router, Switch, Route, } from "react-router-dom"; import Home from './pages/Home'; import Login from './pages/Login'; import Backend from './pages/Backend'; import Admin from './pages/Admin'; function App() { return ( <Router> <Switch> <Route path="/login" component={Login}/> <Route path="/backend" component={Backend}/> <Route path="/admin" component={Admin}/> <Route path="/" component={Home}/> </Switch> </Router> ); } export default App;
然后可以在 Home
頁(yè)面用 Link
加上跳轉(zhuǎn)到其他頁(yè)面的鏈接,這樣就可以跳轉(zhuǎn)了:
import React from 'react'; import { Link } from 'react-router-dom'; function Home() { return ( <> <h1>首頁(yè)</h1> <ul> <li><Link to="/login">登錄</Link></li> <li><Link to="/backend">后臺(tái)</Link></li> <li><Link to="/admin">管理員</Link></li> </ul> </> ); } export default Home;
到現(xiàn)在我們的應(yīng)用運(yùn)行起來(lái)是這樣的:
模塊劃分
雖然我們的跳轉(zhuǎn)實(shí)現(xiàn)了,但是所有人都可以訪問(wèn)任何頁(yè)面,我們前面的需求是要根據(jù)登錄的角色限制訪問(wèn)的頁(yè)面的,在寫代碼前,我們先來(lái)思考下應(yīng)該怎么做這個(gè)。當(dāng)然最直觀最簡(jiǎn)單的方法就是每個(gè)頁(yè)面都檢測(cè)下當(dāng)前用戶的角色,匹配不上就報(bào)錯(cuò)或者跳回首頁(yè)。我們現(xiàn)在只有幾個(gè)頁(yè)面,這樣做好像也還好,但是如果我們的應(yīng)用變大了,頁(yè)面變多了,每個(gè)頁(yè)面都來(lái)一次檢測(cè)就顯得很重復(fù)了,所以我們應(yīng)該換個(gè)角度來(lái)思考這個(gè)問(wèn)題。
仔細(xì)一看,其實(shí)我們總共就三種角色,對(duì)應(yīng)三種不同的權(quán)限,這三個(gè)權(quán)限還有層級(jí)關(guān)系,高級(jí)別的權(quán)限包含了低級(jí)別的權(quán)限,所以我們的頁(yè)面也可以按照這些權(quán)限分為三種:
- 公共頁(yè)面:所有人都可以訪問(wèn),沒(méi)登錄也可以訪問(wèn),包括網(wǎng)站首頁(yè)和登錄頁(yè)
- 普通頁(yè)面:普通登錄用戶可以訪問(wèn)的頁(yè)面
- 管理員頁(yè)面:只有管理員才能訪問(wèn)的頁(yè)面
為了好管理這三種頁(yè)面,我們可以將他們抽取成三個(gè)文件,放到一個(gè)獨(dú)立的文件夾 routes
里面,三個(gè)文件分別命名為 publicRoutes.js
, privateRoutes.js
, adminRoutes.js
:
對(duì)于每個(gè)路由文件,我們可以將這類路由組織成數(shù)組,然后 export
出去給外面調(diào)用,比如 publicRoutes.js
:
import Login from '../pages'; import Home from '../pages/Home'; const publicRoutes = [ { path: '/login', component: Login, exact: true, }, { path: '/', component: Home, exact: true, }, ]; export default publicRoutes;
然后我們外面使用的地方直接改為:
import publicRoutes from './routes/publicRoutes'; function App() { return ( <Router> <Switch> {publicRoutes.map( ({path, component, ...routes}) => <Route key={path} path={path} component={component} {...routes}/> )} <Route path="/backend" component={Backend}/> <Route path="/admin" component={Admin}/> </Switch> </Router> ); }
這樣我們的 App.js
里面就不會(huì)有冗長(zhǎng)的路由路由列表了,而是只需要循環(huán)一個(gè)數(shù)組就行了。但是對(duì)于需要登錄才能訪問(wèn)的頁(yè)面和管理員頁(yè)面我們不能直接渲染 Route
組件,我們最好再封裝一個(gè)高級(jí)組件,將鑒權(quán)的工作放到這個(gè)組件里面去,這樣我們普通的頁(yè)面在實(shí)現(xiàn)時(shí)就不需要關(guān)心怎么鑒權(quán)了。
封裝高級(jí)組件
要封裝這個(gè)鑒權(quán)組件思路也很簡(jiǎn)單,前面我們將 publicRoutes
直接拿來(lái)循環(huán)渲染了 Route
組件,我們的鑒權(quán)組件只需要在這個(gè)基礎(chǔ)上再加一個(gè)邏輯就行了:在渲染真正的 Route
組件前先檢查一下當(dāng)前用戶是否有對(duì)應(yīng)的權(quán)限,如果有就直接渲染 Route
組件,如果沒(méi)有就返回某個(gè)頁(yè)面,可以是登錄頁(yè)或者后臺(tái)首頁(yè),具體根據(jù)自己項(xiàng)目需求來(lái)。所以我們的路由配置文件 privateRoutes.js
, adminRoutes.js
里面的路由會(huì)比 publicRoutes.js
的多兩個(gè)參數(shù):
// privateRoutes.js import Backend from '../pages/Backend'; const privateRoutes = [ { path: '/backend', component: Backend, exact: true, role: 'user', // 當(dāng)前路由需要的角色權(quán)限 backUrl: '/login' // 不滿足權(quán)限跳轉(zhuǎn)的路由 }, ]; export default privateRoutes;
adminRoutes.js
是類似的寫法:
// adminRoutes.js import Admin from '../pages/Admin'; const adminRoutes = [ { path: '/admin', component: Admin, exact: true, role: 'admin', // 需要的權(quán)限是admin backUrl: '/backend' // 不滿足權(quán)限跳回后臺(tái)頁(yè)面 }, ]; export default adminRoutes;
然后就可以寫我們的高級(jí)組件了,我們將它命名為 AuthRoute
吧,注意我們這里假設(shè)的用戶登錄時(shí)后端API會(huì)返回給我們當(dāng)前用戶的角色,一個(gè)用戶可能有多個(gè)角色,比如普通用戶的角色是 ['user']
,管理員的角色是 ['user', 'admin']
,具體的權(quán)限驗(yàn)證邏輯要看自己項(xiàng)目權(quán)限的設(shè)計(jì),這里只是一個(gè)例子:
// AuthRoute.js import React from 'react'; import { Route, Redirect } from 'react-router-dom'; function AuthRoute(props) { const { user: { role: userRole }, role: routeRole, backUrl, ...otherProps } = props; // 如果用戶有權(quán)限,就渲染對(duì)應(yīng)的路由 if (userRole && userRole.indexOf(routeRole) > -1) { return <Route {...otherProps} /> } else { // 如果沒(méi)有權(quán)限,返回配置的默認(rèn)路由 return <Redirect to={backUrl} /> } } export default AuthRoute;
然后用我們的 AuthRoute
的渲染 adminRoutes
和 privateRoutes
:
// ... 省略其他代碼 ... {privateRoutes.map( (route) => <AuthRoute key={route.path} {...route}/> )} {adminRoutes.map( (route) => <AuthRoute key={route.path} {...route}/> )}
登錄設(shè)置權(quán)限
在我們的 AuthRoute
里面用到了 user: { role }
這個(gè)變量,但是我們還沒(méi)設(shè)置它。真實(shí)項(xiàng)目中一般是登錄的時(shí)候后端API會(huì)返回當(dāng)前用戶的角色,然后前端將這個(gè)權(quán)限信息保存在一些狀態(tài)管理工具里面,比如 Redux
。我們這里直接在 Login
頁(yè)面寫死兩個(gè)按鈕來(lái)模擬這個(gè)權(quán)限了,用戶的配置就用根組件的 state
來(lái)管理了, Login
頁(yè)面的兩個(gè)按鈕會(huì)改變對(duì)應(yīng)的 state
:
import React from 'react'; import { Link } from 'react-router-dom'; function Login(props) { const {loginAsUser, loginAsAdmin, history} = props; const userLoginHandler = () => { loginAsUser(); // 調(diào)用父級(jí)方法設(shè)置用戶權(quán)限 history.replace('/backend'); // 登錄后跳轉(zhuǎn)后臺(tái)頁(yè)面 } const adminLoginHandler = () => { loginAsAdmin(); // 調(diào)用父級(jí)方法設(shè)置管理員權(quán)限 history.replace('/admin'); // 登錄后跳轉(zhuǎn)管理員頁(yè)面 } return ( <> <h1>登錄頁(yè)</h1> <button onClick={userLoginHandler}>普通用戶登錄</button> <br/><br/> <button onClick={adminLoginHandler}>管理員登錄</button> <br/><br/> <Link to="/">回首頁(yè)</Link> </> ); } export default Login;
到這里我們這個(gè)簡(jiǎn)單的路由鑒權(quán)就完成了,具體跑起來(lái)效果如下:
本文全部代碼已經(jīng)上傳GitHub,大家可以拿下來(lái)玩玩: https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-router-usage
總結(jié)
React-Router
可以用來(lái)管理前端的路由跳轉(zhuǎn),是React
生態(tài)里面很重要的一個(gè)庫(kù)。React-Router
為了同時(shí)支持瀏覽器和React-Native
,他分拆成了三個(gè)包react-router
核心包,react-router-dom
瀏覽器包,react-router-native
支持React-Native
。使用時(shí)不需要引入react-router
,只需要引入需要的平臺(tái)包就行。- 對(duì)于需要不同權(quán)限的路由,我們可以將他們拎出來(lái)分好類,單獨(dú)建成一個(gè)文件,如果路由不多,放在一個(gè)文件導(dǎo)出多個(gè)數(shù)組也行。
- 對(duì)于需要鑒權(quán)的路由,我們可以用一個(gè)高級(jí)組件將權(quán)限校驗(yàn)的邏輯封裝在里面,其他頁(yè)面只需要加好配置,完全不用關(guān)心鑒權(quán)的問(wèn)題。
本文內(nèi)容偏簡(jiǎn)單,作為熟悉 React-Router
的用法還不錯(cuò),但是我們不能只會(huì)用,還要知道他的原理。下篇文章我們就來(lái)看看 React-Router
的源碼里面蘊(yùn)藏了什么奧秘,大家可以點(diǎn)個(gè)關(guān)注不迷路,哈哈~
參考資料
官方文檔:https://reactrouter.com/web/guides/quick-start
GitHub源碼地址:https://juejin.im/post/5e3ffc85518825494e2772fd
到此這篇關(guān)于使用React-Router實(shí)現(xiàn)前端路由鑒權(quán)的示例代碼的文章就介紹到這了,更多相關(guān)React-Router 前端路由鑒權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解React開發(fā)中使用require.ensure()按需加載ES6組件
本篇文章主要介紹了詳解React開發(fā)中使用require.ensure()按需加載ES6組件,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05react學(xué)習(xí)每天一個(gè)hooks?useWhyDidYouUpdate
這篇文章主要為大家介紹了react學(xué)習(xí)每天一個(gè)hooks?useWhyDidYouUpdate使用示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04react-native使用leanclound消息推送的方法
這篇文章主要介紹了react-native使用leanclound消息推送的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08react-router-dom?V6的配置使用實(shí)踐
本文主要介紹了react-router-dom?V6的配置使用實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05詳解react-navigation6.x路由庫(kù)的基本使用
最近兩個(gè)項(xiàng)目都用到了React Navigation,所以就研究一下如何使用,本文主要介紹了react-navigation6.x路由庫(kù)的基本使用,感興趣的可以了解一下2021-11-11