在使用 Vue 或者 Angular 的時候究反,框架提供了路由守衛(wèi)功能,用來在進入某個路有前進行一些校驗工作,如果校驗失敗输枯,就跳轉(zhuǎn)到 404 或者登陸頁面,比如 Vue 中的 beforeEnter
函數(shù):
...
router.beforeEach(async(to, from, next) => {
const toPath = to.path;
const fromPath = from.path;
})
...
在之前的版本中占贫,React Router 也提供了類似的 onEnter
鉤子桃熄,但在 React Router 4.0 版本中,取消了這個方法型奥。React Router 4.0 采用了聲明式的組件瞳收,路由即組件,要實現(xiàn)路由守衛(wèi)功能厢汹,就得我們自己去寫了螟深。
如果不使用路由守衛(wèi),Router 組件是這樣子的:
import * as React from 'react';
import { HashRouter,Switch,Route,Redirect } from 'react-router-dom';
import { HomePage } from '../pages/home/home.page';
import { LoginPage } from '../pages/login/login.page';
import { ErrorPage } from '../pages/error/error.page';
export const Router = () => (
<HashRouter>
<Switch>
<Route path="/" exact component={HomePage}/>
<Route path="/login" exact component={LoginPage}/>
<Route path="/home" exact component={HomePage}/>
<Route path="/404" exact component={ErrorPage}/>
<Redirect to="/404" />
</Switch>
</HashRouter>
);
上面的 Router 組件坑匠,包含了三個頁面:
- 登陸
- 主頁
- 404 頁面
以及四個路由:
- 根路由
- 登陸路由
- 主頁路由
- 404 路由
其中血崭,根路由和 /home
路由,都定向到了主頁路由。
以上是一個基本的路由定義夹纫,可以在登陸/主頁和 404 頁面之間來回跳轉(zhuǎn)咽瓷,但也有一些問題:
- 非登陸狀態(tài)下,可以直接跳轉(zhuǎn)到主頁
- 登陸狀態(tài)下舰讹,也可以輸入
/login
路由跳轉(zhuǎn)到登錄頁
現(xiàn)在茅姜,我們想完成這樣的功能:
- 非登陸狀態(tài)下,無法直接跳轉(zhuǎn)到主頁月匣,如果在非登陸狀態(tài)下進行主頁跳轉(zhuǎn)钻洒,需要重定向至登陸路由
- 登陸狀態(tài)下,無法跳轉(zhuǎn)至登錄頁锄开,如果在登陸狀態(tài)下進行登陸頁跳轉(zhuǎn)素标,需要重定向至主頁路由
要完成這個功能,有兩種方案:
- 在每個組件中萍悴,根據(jù)
props
上的history
對象來進行跳轉(zhuǎn) - 進行全局的路由守衛(wèi)處理
第一種方式头遭,實現(xiàn)起來比較簡單,但有很多的代碼量癣诱,這里主要介紹第二種方式计维。
在 React Router 4.0 中,沒有再像之前的版本那樣撕予,提供 onEnter
這樣的全局跳轉(zhuǎn)鉤子鲫惶,因此要通過高階組件的方式去處理。
下面是我的實現(xiàn)方式实抡,首先欠母,準(zhǔn)備一份路由表,包含了路由的地址澜术,組件以及是否需要權(quán)限校驗:
import { HomePage } from '../pages/home/home.page';
import { LoginPage } from '../pages/login/login.page';
import { ErrorPage } from '../pages/error/error.page';
interface routerConfigModel {
path:string,
component?:any,
auth?:boolean
}
export const routerConfig:routerConfigModel[] = [
{
path:'/',
component:HomePage,
auth:true,
},{
path:'/home',
component:HomePage,
auth:true,
},{
path:'/login',
component:LoginPage,
},{
path:'/404',
component:ErrorPage
}
];
將 auth
設(shè)置為 true
艺蝴,表示該路由需要權(quán)限校驗猬腰。
然后鸟废,定義 Router
組件,該組件是經(jīng)過高階組件包裝后的結(jié)果:
import * as React from 'react';
import { HashRouter,Switch } from 'react-router-dom';
import { FrontendAuth } from '../components/frontend-auth/frontend-auth.component'
import { routerConfig } from './router.config'
export class Router extends React.Component{
render(){
return(
<HashRouter>
<Switch>
<FrontendAuth config={routerConfig} />
</Switch>
</HashRouter>
);
}
}
所有的路由跳轉(zhuǎn)姑荷,都交給 FrontendAuth
高階組件代理完成盒延。下面是 FrontendAuth
組件的實現(xiàn):
import * as React from 'react';
import { Route,Redirect } from 'react-router-dom';
import { propsModel } from './frontend-auth.model'
export class FrontendAuth extends React.Component<any,propsModel>{
render(){
const { location,config } = this.props;
const { pathname } = location;
const isLogin = localStorage.getItem('__config_center_token')
// 如果該路由不用進行權(quán)限校驗,登錄狀態(tài)下登陸頁除外
// 因為登陸后鼠冕,無法跳轉(zhuǎn)到登陸頁
// 這部分代碼添寺,是為了在非登陸狀態(tài)下,訪問不需要權(quán)限校驗的路由
const targetRouterConfig = config.find((v:any) => v.path === pathname);
if(targetRouterConfig && !targetRouterConfig.auth && !isLogin){
const { component } = targetRouterConfig;
return <Route exact path={pathname} component={component} />
}
if(isLogin){
// 如果是登陸狀態(tài)懈费,想要跳轉(zhuǎn)到登陸计露,重定向到主頁
if(pathname === '/login'){
return <Redirect to='/' />
}else{
// 如果路由合法,就跳轉(zhuǎn)到相應(yīng)的路由
if(targetRouterConfig){
return <Route path={pathname} component={targetRouterConfig.component} />
}else{
// 如果路由不合法,重定向到 404 頁面
return <Redirect to='/404' />
}
}
}else{
// 非登陸狀態(tài)下票罐,當(dāng)路由合法時且需要權(quán)限校驗時叉趣,跳轉(zhuǎn)到登陸頁面,要求登陸
if(targetRouterConfig && targetRouterConfig.auth){
return <Redirect to='/login' />
}else{
// 非登陸狀態(tài)下该押,路由不合法時疗杉,重定向至 404
return <Redirect to='/404' />
}
}
}
}
以及對應(yīng)的 Model:
export interface propsModel {
config:any[],
}
頁面上的路由跳轉(zhuǎn),都由 FrontendAuth
高階組件代理了蚕礼,在 Switch
組件內(nèi)部烟具,不再是 Route
組件,而只有一個 FrontendAuth
組件奠蹬。
FrontendAuth
組件接收一個名為 config
的 Props
朝聋,這是一份路由表。同時囤躁,由于 FrontendAuth
組件放在了 Switch
組件內(nèi)部玖翅,React Router 還自動為 FrontendAuth
注入了 location
屬性,當(dāng)?shù)刂窓诘穆酚砂l(fā)生變化時割以,就會觸發(fā) location
屬性對象上的 pathname
屬性發(fā)生變化金度,從而觸發(fā) FrontendAuth
的更新(調(diào)用 render
函數(shù))。
FrontendAuth
的 render
函數(shù)中严沥,根據(jù) pathname
查找到路由表中的相關(guān)配置猜极,如果該配置中指定了無需校驗,就直接返回相應(yīng)的 Route
組件消玄。
如果查找到的配置需要進行校驗跟伏,再根據(jù)是否登陸進行處理,具體可以查看代碼中的注釋翩瓜。
總結(jié)一下受扳,實現(xiàn)路由守衛(wèi)需要考慮到以下的問題:
- 未登錄情況下,訪問不需要權(quán)限校驗的合法頁面:允許訪問
- 登陸情況下兔跌,訪問登陸頁面:禁止訪問勘高,跳轉(zhuǎn)至主頁
- 登陸情況下,訪問除登陸頁以外的合法頁面:允許訪問
- 登陸情況下坟桅,訪問所有的非法頁面:禁止訪問华望,跳轉(zhuǎn)至 404
- 未登錄情況下,訪問需要權(quán)限校驗的頁面:禁止訪問仅乓,跳轉(zhuǎn)至登陸頁
- 未登錄情況下赖舟,訪問所有的非法頁面:禁止訪問,跳轉(zhuǎn)至 404
注夸楣,上面的 FrontendAuth
高階組件宾抓,實際上是一個代理型的高階組件子漩,這部分內(nèi)容可以查看我的這篇文章。
完石洗。