React Router 4.0 實現(xiàn)路由守衛(wèi)

在使用 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 組件接收一個名為 configProps朝聋,這是一份路由表。同時囤躁,由于 FrontendAuth 組件放在了 Switch 組件內(nèi)部玖翅,React Router 還自動為 FrontendAuth 注入了 location 屬性,當(dāng)?shù)刂窓诘穆酚砂l(fā)生變化時割以,就會觸發(fā) location 屬性對象上的 pathname 屬性發(fā)生變化金度,從而觸發(fā) FrontendAuth 的更新(調(diào)用 render 函數(shù))。
FrontendAuthrender 函數(shù)中严沥,根據(jù) pathname 查找到路由表中的相關(guān)配置猜极,如果該配置中指定了無需校驗,就直接返回相應(yīng)的 Route 組件消玄。
如果查找到的配置需要進行校驗跟伏,再根據(jù)是否登陸進行處理,具體可以查看代碼中的注釋翩瓜。

總結(jié)一下受扳,實現(xiàn)路由守衛(wèi)需要考慮到以下的問題:

  1. 未登錄情況下,訪問不需要權(quán)限校驗的合法頁面:允許訪問
  2. 登陸情況下兔跌,訪問登陸頁面:禁止訪問勘高,跳轉(zhuǎn)至主頁
  3. 登陸情況下,訪問除登陸頁以外的合法頁面:允許訪問
  4. 登陸情況下坟桅,訪問所有的非法頁面:禁止訪問华望,跳轉(zhuǎn)至 404
  5. 未登錄情況下,訪問需要權(quán)限校驗的頁面:禁止訪問仅乓,跳轉(zhuǎn)至登陸頁
  6. 未登錄情況下赖舟,訪問所有的非法頁面:禁止訪問,跳轉(zhuǎn)至 404

注夸楣,上面的 FrontendAuth 高階組件宾抓,實際上是一個代理型的高階組件子漩,這部分內(nèi)容可以查看我的這篇文章

完石洗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末痛单,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子劲腿,更是在濱河造成了極大的恐慌旭绒,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焦人,死亡現(xiàn)場離奇詭異挥吵,居然都是意外死亡,警方通過查閱死者的電腦和手機花椭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門忽匈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人矿辽,你說我怎么就攤上這事丹允。” “怎么了袋倔?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵雕蔽,是天一觀的道長。 經(jīng)常有香客問我宾娜,道長批狐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任前塔,我火速辦了婚禮嚣艇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘华弓。我一直安慰自己食零,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布寂屏。 她就那樣靜靜地躺著贰谣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凑保。 梳的紋絲不亂的頭發(fā)上冈爹,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天涌攻,我揣著相機與錄音欧引,去河邊找鬼。 笑死恳谎,一個胖子當(dāng)著我的面吹牛芝此,可吹牛的內(nèi)容都是我干的憋肖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼婚苹,長吁一口氣:“原來是場噩夢啊……” “哼岸更!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膊升,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤怎炊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后廓译,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體评肆,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年非区,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓜挽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡征绸,死狀恐怖久橙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情管怠,我是刑警寧澤淆衷,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站渤弛,受9級特大地震影響吭敢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜暮芭,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一鹿驼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辕宏,春花似錦畜晰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至聚假,卻和暖如春块蚌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膘格。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工峭范, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瘪贱。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓纱控,卻偏偏與公主長得像辆毡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子甜害,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容