在使用 Vue 或者 Angular 的時候,框架提供了路由守衛功能,用來在進入某個路有前進行一些校驗工作,如果校驗失敗,就跳轉到 404 或者登陸頁面,比如 Vue 中的 beforeEnter
函數:
...
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 采用了聲明式的組件,路由即組件,要實現路由守衛功能,就得我們自己去寫了。
如果不使用路由守衛,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 頁面之間來回跳轉,但也有一些問題:
- 非登陸狀態下,可以直接跳轉到主頁
- 登陸狀態下,也可以輸入
/login
路由跳轉到登錄頁
現在,我們想完成這樣的功能:
- 非登陸狀態下,無法直接跳轉到主頁,如果在非登陸狀態下進行主頁跳轉,需要重定向至登陸路由
- 登陸狀態下,無法跳轉至登錄頁,如果在登陸狀態下進行登陸頁跳轉,需要重定向至主頁路由
要完成這個功能,有兩種方案:
- 在每個組件中,根據
props
上的history
對象來進行跳轉 - 進行全局的路由守衛處理
第一種方式,實現起來比較簡單,但有很多的代碼量,這里主要介紹第二種方式。
在 React Router 4.0 中,沒有再像之前的版本那樣,提供 onEnter
這樣的全局跳轉鈎子,因此要通過高階組件的方式去處理。
下面是我的實現方式,首先,准備一份路由表,包含了路由的地址,組件以及是否需要權限校驗:
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
設置為 true
,表示該路由需要權限校驗。
然后,定義 Router
組件,該組件是經過高階組件包裝后的結果:
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> ); } }
所有的路由跳轉,都交給 FrontendAuth
高階組件代理完成。下面是 FrontendAuth
組件的實現:
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') // 如果該路由不用進行權限校驗,登錄狀態下登陸頁除外 // 因為登陸后,無法跳轉到登陸頁 // 這部分代碼,是為了在非登陸狀態下,訪問不需要權限校驗的路由 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){ // 如果是登陸狀態,想要跳轉到登陸,重定向到主頁 if(pathname === '/login'){ return <Redirect to='/' /> }else{ // 如果路由合法,就跳轉到相應的路由 if(targetRouterConfig){ return <Route path={pathname} component={targetRouterConfig.component} /> }else{ // 如果路由不合法,重定向到 404 頁面 return <Redirect to='/404' /> } } }else{ // 非登陸狀態下,當路由合法時且需要權限校驗時,跳轉到登陸頁面,要求登陸 if(targetRouterConfig && targetRouterConfig.auth){ return <Redirect to='/login' /> }else{ // 非登陸狀態下,路由不合法時,重定向至 404 return <Redirect to='/404' /> } } } }
以及對應的 Model:
export interface propsModel { config:any[], }
頁面上的路由跳轉,都由 FrontendAuth
高階組件代理了,在 Switch
組件內部,不再是 Route
組件,而只有一個 FrontendAuth
組件。
FrontendAuth
組件接收一個名為 config
的 Props
,這是一份路由表。同時,由於 FrontendAuth
組件放在了 Switch
組件內部,React Router 還自動為 FrontendAuth
注入了 location
屬性,當地址欄的路由發生變化時,就會觸發 location
屬性對象上的 pathname
屬性發生變化,從而觸發 FrontendAuth
的更新(調用 render
函數)。
FrontendAuth
的 render
函數中,根據 pathname
查找到路由表中的相關配置,如果該配置中指定了無需校驗,就直接返回相應的 Route
組件。
如果查找到的配置需要進行校驗,再根據是否登陸進行處理,具體可以查看代碼中的注釋。
總結一下,實現路由守衛需要考慮到以下的問題:
- 未登錄情況下,訪問不需要權限校驗的合法頁面:允許訪問
- 登陸情況下,訪問登陸頁面:禁止訪問,跳轉至主頁
- 登陸情況下,訪問除登陸頁以外的合法頁面:允許訪問
- 登陸情況下,訪問所有的非法頁面:禁止訪問,跳轉至 404
- 未登錄情況下,訪問需要權限校驗的頁面:禁止訪問,跳轉至登陸頁
- 未登錄情況下,訪問所有的非法頁面:禁止訪問,跳轉至 404
作者:黑黢黢
鏈接:https://www.jianshu.com/p/677433245697
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。