React-Router
是React生態裏面很重要的一環,如今React的單頁應用的路由基本都是前端本身管理的,而不像之前是後端路由,React管理路由的庫經常使用的就是React-Router
。本文想寫一下React-Router
的使用,可是光介紹API又太平淡了,並且官方文檔已經寫得很好了,我這裏就用一個常見的開發場景來看看React-Router
是怎麼用的吧。咱們通常的系統都會有用戶訪問權限的限制,某些頁面可能須要用戶具備必定的權限才能訪問。本文就是用React-Router
來實現一個前端鑑權模型。javascript
本文所有代碼已經上傳GitHub,你們能夠拿下來玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-router-usage前端
本文要實現的功能是你們常常遇到的場景,就是要控制不一樣的用戶角色來訪問不一樣的頁面,這裏總共有四個頁面:java
/index
: 網站首頁/login
: 登陸頁/backend
:後臺頁面/admin
:管理頁面
另外還有三種角色:react
未登陸用戶
:只能訪問網站首頁/index
和登陸頁/login
普通用戶
:能夠訪問網站首頁/index
,登陸頁/login
和後臺頁面/backend
管理員
:能夠訪問管理頁面/admin
和其餘全部頁面
要實現路由鑑權,咱們還得一步一步來,咱們先用React-Router搭建一個簡單的帶有這幾個頁面的項目。咱們直接用create-react-app
建立一個新項目,而後建了一個pages
文件夾,裏面放入咱們前面說的那幾個頁面:git
咱們頁面先寫簡單點,先寫個標題吧,好比這樣:github
import React from 'react'; function Admin() { return ( <h1>管理員頁面</h1> ); }
其餘幾個頁面也是相似的。web
而後咱們就能夠在App.js
裏面引入React-Router
作路由跳轉了,注意咱們在瀏覽器上使用的是react-router-dom
,新版的React-Router
將核心邏輯層和展現層分開了,核心邏輯會處理路由匹配等,展現層會處理實際的跳轉和路由變化的監聽,之因此這麼分,是由於React-Router不只僅須要支持瀏覽器,還須要支持React Native,這兩個平臺的監聽和跳轉是不同的,因此如今React-Router下面有好幾個包了:後端
react-router
:核心邏輯處理,提供一些公用的基類
react-router-dom
:具體實現瀏覽器相關的路由監聽和跳轉數組
react-router-native
:具體實現RN相關的路由監聽和跳轉瀏覽器
在實際使用時,咱們通常不須要引用react-router
,而是直接用react-router-dom
就行,由於它本身會去引用react-router
。下面咱們在項目裏面引入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
頁面用Link
加上跳轉到其餘頁面的連接,這樣就能夠跳轉了:
import React from 'react'; import { Link } from 'react-router-dom'; function Home() { return ( <> <h1>首頁</h1> <ul> <li><Link to="/login">登陸</Link></li> <li><Link to="/backend">後臺</Link></li> <li><Link to="/admin">管理員</Link></li> </ul> </> ); } export default Home;
到如今咱們的應用運行起來是這樣的:
雖然咱們的跳轉實現了,可是全部人均可以訪問任何頁面,咱們前面的需求是要根據登陸的角色限制訪問的頁面的,在寫代碼前,咱們先來思考下應該怎麼作這個。固然最直觀最簡單的方法就是每一個頁面都檢測下當前用戶的角色,匹配不上就報錯或者跳回首頁。咱們如今只有幾個頁面,這樣作好像也還好,可是若是咱們的應用變大了,頁面變多了,每一個頁面都來一次檢測就顯得很重複了,因此咱們應該換個角度來思考這個問題。
仔細一看,其實咱們總共就三種角色,對應三種不一樣的權限,這三個權限還有層級關係,高級別的權限包含了低級別的權限,因此咱們的頁面也能夠按照這些權限分爲三種:
公共頁面
:全部人均可以訪問,沒登陸也能夠訪問,包括網站首頁和登陸頁普通頁面
:普通登陸用戶能夠訪問的頁面管理員頁面
:只有管理員才能訪問的頁面
爲了好管理這三種頁面,咱們能夠將他們抽取成三個文件,放到一個獨立的文件夾routes
裏面,三個文件分別命名爲publicRoutes.js
,privateRoutes.js
,adminRoutes.js
:
對於每一個路由文件,咱們能夠將這類路由組織成數組,而後export
出去給外面調用,好比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
裏面就不會有冗長的路由路由列表了,而是隻須要循環一個數組就好了。可是對於須要登陸才能訪問的頁面和管理員頁面咱們不能直接渲染Route
組件,咱們最好再封裝一個高級組件,將鑑權的工做放到這個組件裏面去,這樣咱們普通的頁面在實現時就不須要關心怎麼鑑權了。
要封裝這個鑑權組件思路也很簡單,前面咱們將publicRoutes
直接拿來循環渲染了Route
組件,咱們的鑑權組件只須要在這個基礎上再加一個邏輯就好了:在渲染真正的Route
組件前先檢查一下當前用戶是否有對應的權限,若是有就直接渲染Route
組件,若是沒有就返回某個頁面,能夠是登陸頁或者後臺首頁,具體根據本身項目需求來。因此咱們的路由配置文件privateRoutes.js
,adminRoutes.js
裏面的路由會比publicRoutes.js
的多兩個參數:
// privateRoutes.js import Backend from '../pages/Backend'; const privateRoutes = [ { path: '/backend', component: Backend, exact: true, role: 'user', // 當前路由須要的角色權限 backUrl: '/login' // 不知足權限跳轉的路由 }, ]; export default privateRoutes;
adminRoutes.js
是相似的寫法:
// adminRoutes.js import Admin from '../pages/Admin'; const adminRoutes = [ { path: '/admin', component: Admin, exact: true, role: 'admin', // 須要的權限是admin backUrl: '/backend' // 不知足權限跳回後臺頁面 }, ]; export default adminRoutes;
而後就能夠寫咱們的高級組件了,咱們將它命名爲AuthRoute
吧,注意咱們這裏假設的用戶登陸時後端API會返回給咱們當前用戶的角色,一個用戶可能有多個角色,好比普通用戶的角色是['user']
,管理員的角色是['user', 'admin']
,具體的權限驗證邏輯要看本身項目權限的設計,這裏只是一個例子:
// 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; // 若是用戶有權限,就渲染對應的路由 if (userRole && userRole.indexOf(routeRole) > -1) { return <Route {...otherProps} /> } else { // 若是沒有權限,返回配置的默認路由 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}/> )}
在咱們的AuthRoute
裏面用到了user: { role }
這個變量,可是咱們還沒設置它。真實項目中通常是登陸的時候後端API會返回當前用戶的角色,而後前端將這個權限信息保存在一些狀態管理工具裏面,好比Redux
。咱們這裏直接在Login
頁面寫死兩個按鈕來模擬這個權限了,用戶的配置就用根組件的state
來管理了,Login
頁面的兩個按鈕會改變對應的state
:
import React from 'react'; import { Link } from 'react-router-dom'; function Login(props) { const {loginAsUser, loginAsAdmin, history} = props; const userLoginHandler = () => { loginAsUser(); // 調用父級方法設置用戶權限 history.replace('/backend'); // 登陸後跳轉後臺頁面 } const adminLoginHandler = () => { loginAsAdmin(); // 調用父級方法設置管理員權限 history.replace('/admin'); // 登陸後跳轉管理員頁面 } return ( <> <h1>登陸頁</h1> <button onClick={userLoginHandler}>普通用戶登陸</button> <br/><br/> <button onClick={adminLoginHandler}>管理員登陸</button> <br/><br/> <Link to="/">回首頁</Link> </> ); } export default Login;
到這裏咱們這個簡單的路由鑑權就完成了,具體跑起來效果以下:
本文所有代碼已經上傳GitHub,你們能夠拿下來玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-router-usage
React-Router
能夠用來管理前端的路由跳轉,是React
生態裏面很重要的一個庫。React-Router
爲了同時支持瀏覽器和React-Native
,他分拆成了三個包react-router
核心包,react-router-dom
瀏覽器包,react-router-native
支持React-Native
。使用時不須要引入react-router
,只須要引入須要的平臺包就行。本文內容偏簡單,做爲熟悉React-Router
的用法還不錯,可是咱們不能只會用,還要知道他的原理。下篇文章咱們就來看看React-Router
的源碼裏面蘊藏了什麼奧祕,你們能夠點個關注不迷路,哈哈~
官方文檔:https://reactrouter.com/web/guides/quick-start
GitHub源碼地址:https://github.com/ReactTraining/react-router/tree/master/packages
文章的最後,感謝你花費寶貴的時間閱讀本文,若是本文給了你一點點幫助或者啓發,請不要吝嗇你的贊和GitHub小星星,你的支持是做者持續創做的動力。
做者博文GitHub項目地址: https://github.com/dennis-jiang/Front-End-Knowledges