Reacr-Router 是React生態裏面很重要的一環,如今React的單頁應用的路由由基本都是前端自已管理的,而不像之前是後端路由,React管理路由的庫經常使用的就是 React-Router。本文想寫一下 React-Router的使用,可是光介紹API又太平淡了。並且官方文檔已經寫的很好了,我這裏就用一個常見的開發場景來看看React-Router是怎麼用的吧。咱們通常的系統都會有用戶訪問權限的限制,某些頁面可能須要用戶具備必定的權限才能訪問。本文就是用React-Router來實現一個前端鑑權模型。前端
本文要實現的功能是你們常常遇到的場景,就是要控制不一樣的用戶角色來訪問不一樣的頁面,這裏總共有四個頁面:react
/index : 網站首頁後端
/login : 登陸頁數組
/backend : 後臺頁面瀏覽器
/admin : 管理頁面markdown
另外還有三種角色:react-router
未登陸用戶 : 只能訪問網站首頁/index和登陸頁/loginapp
普通用戶 : 能夠訪問網站首頁/index,登陸頁/login和後臺頁面/backenddom
管理員 : 能夠訪問管理頁面/admin 和其餘全部頁面工具
要實現路由鑑權,咱們還得一步一步來,咱們先用React-Router搭建一個簡單的帶有這幾個頁面的項目。咱們直接用create-react-app建立一個新項目,而後建一個pages文件夾,裏面放入咱們前面說說的那幾個頁面:
咱們頁面先寫簡單點,先寫個標題吧,好比這樣:
import React from 'react';
function Admin() {
return (
<h1>管理員頁面</h1>
);
}
複製代碼
其餘幾個頁面也是相似的。
而後咱們就能夠在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';
function Admin() {
return (
<h1>管理員頁面</h1>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;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;
複製代碼
到如今咱們的應用運行起來是這樣的:
雖然咱們的跳轉實現了,可是全部人均可以訪問任何頁面,咱們前面的須要是要根據登陸的角色限制訪問的頁面的,在寫代碼前,咱們先來思考下應該怎麼作這個。固然最直觀最簡單的方法就是每一個頁面都檢測下當前用戶的角色,匹配不上就報錯或者跳回首頁。咱們如今只有幾個頁面,這樣作好像也還好,可是若是咱們的應用變大了,頁面變多了,每一個頁面都來一次檢測就顯得很重複了,因此咱們應該換個角度來思考這個問題。
仔細一看,其實咱們總共就三種角色,對應三種不一樣的權限,這三個權限還有層級關係,高級別的權限包含了低級別的權限,因此咱們的頁面也能夠按照這些權限分爲三種:
公共頁面 : 全部人均可以訪問,沒登陸也能夠訪問,包括網站首頁和登陸頁
普通頁面 : 普通登陸用戶能夠訪問的頁面
管理員頁面 : 只有管理員才能訪問的頁面
爲了好管理這三種頁面,咱們能夠將它們抽取成三個文件,放到一個獨立的文件夾router裏面,三個文件分別命名爲: 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裏面就不會有冗長的路由路由列表了,而是隻須要循環一個數組就好了。可是對於須要登陸才能訪問的頁面和管理員頁面咱們不能直接渲染Router組件,咱們最好再封裝一個高級組件,將鑑權的工做放到這個組件裏面去,這樣咱們普通的頁面在實現時就不須要關心怎麼鑑權了。
要封裝這個鑑權組件思路也很簡單,前面咱們將publicRoutes直接拿來循環渲染了Router組件,咱們鑑權組件只須要在這個基礎上再加一個邏輯就好了:在渲染真正的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頁面的兩個按鈕會改變對應的stare:
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;
複製代碼
到這裏咱們這個簡單的路由鑑權就完成了,具體跑起來的效果以下:
2.React-Router爲了同時支持瀏覽器和React-Native,他分拆成了三個包react-router核心包,react-router-dom瀏覽器包,react-router-native支持React-Native。使用時不須要引入react-router,只須要引入須要的平臺包就行。
對於須要不一樣權限的路由,咱們能夠將他們拎出來分好類,單獨建成一個文件,若是路由很少,放在一個文件導出多個數組也行。
對於須要鑑權的路由,咱們能夠用一個高級組件將權限校驗的邏輯封裝在裏面,其餘頁面只須要加好配置,徹底不用關心鑑權的問題。