在咱們本身實現react-router的路由攔截以前,咱們先看一下vue的路由攔截是怎麼使用的,都作到了哪些事情:javascript
正如其名,vue-router 提供的導航守衛主要用來經過跳轉或取消的方式守衛導航。vue
你可使用 router.beforeEach 註冊一個全局前置守衛:java
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
複製代碼
當一個導航觸發時,全局前置守衛按照建立順序調用。守衛是異步解析執行,此時導航在全部守衛 resolve 完以前一直處於 等待中。react
在這裏,咱們能夠看到,vue在全部的路由跳轉前,在beforeEach中能夠監聽全部的路由跳轉,若是符合規則,就進行跳轉,若是不符合,那麼就跳轉到我指定的位置。git
react-router當中,爲何不能提供一樣的api呢?做者在github中回覆到:github
You can do this from within your render function. JSX doesn't need an API for this because it's more flexible.
複製代碼
大概的意思就是就是:vue-router
您能夠在渲染功能中執行此操做。JSX不須要API,由於它更靈活。
複製代碼
連接:react-router路由攔截官方說明npm
在做者的回覆當中能夠看到,他但願react-router是靈活的,不但願在裏面添加太多的api,這些api應該是讓使用者,根據本身的需求去實現本身的路由攔截。下面,就開始實現一個本身的,能夠知足大部分需求的路由攔截。redux
react-router版本:4.0api
首先,咱們要使用react-router-config,用數組的形式去寫一個路由的配置:
//routerConfig.js
const routes = [
{
path: '/',
component: 'component/app',
routes: [
{
path: '/asd',
component: 'component/topics',
routes: [
{
path: '/asd/login',
component: 'component/home'
}
]
}
]
}
]
export default routes
複製代碼
用配置的方式寫,是由於這樣能夠很直觀的看出來,咱們整個項目的路由配置,知道咱們具體要跳轉到什麼位置,在什麼位置下,會顯示什麼樣的組件。
應該能夠看出來,這裏面我寫的兩個地方,和文檔是有區別的:
1.我整個數組只有一個列表項,只有這個一個對象。
2.個人compoent的值是字符串,而不是一個對象或者方法
複製代碼
第一點:由於,咱們可能要在當前的頁面中,須要一個根路由,在根路由中,咱們要可能作一些相似與主題顏色的設定,全局內容的展現之類的操做,在這裏,咱們就能夠作到了,剩下的,都在他的routes裏面去作就ok了。
第二點:這麼作的目的,就是爲了實現路由更快的渲染,在正常的使用方式中,咱們通常都是這樣的:
//僞代碼,僅供參考
import A from './a'
{
path:'/',
component:A
}
複製代碼
基本上是這樣實現的,這樣實現是有一個問題,若是咱們的頁面,有20哥,甚至50個、100個要跳轉的路徑,怎麼辦,這樣咱們每次加載到router.js這個文件的時候,是須要把這麼多的文件都import過來,這樣是很影響代碼的執行效率和頁面的渲染速度的。
具體怎麼渲染,咱們會在下面去一一細分。
因爲,路由的總體,是一個數組(array),咱們要在這裏,去作一個循環,若是咱們用最笨重的方式去實現
<Route path='/' component={a} />
<Route path='/b' component={b} />
<Route path='/c' component={c} />
<Route path='/d' component={d} />
...
複製代碼
很明顯,這個樣子去實現一個路由的跳轉,是不明智的,咱們要寫的是一個可維護,易讀性強的路由。
因此,我在這裏寫了一個用來遍歷路由數組的方法。
//renderRoutesMap.js
import RouterGuard from './routerGuard'
const renderRoutesMap = (routes) => (
routes.map((route, index) => {
return (
<Route key={index} path={route.path} render={props => (
<RouterGuard {...route} {...props} />
)}
/>
)
})
)
export default renderRoutesMap
複製代碼
這裏咱們把路由的渲染作了一個小小的遍歷,把咱們的路由對象,去作了一次遍歷,重點來了!!!
RouterGuard就是咱們的重點,在這裏,咱們就要作真正的,路由攔截(導航守衛)!
//routerGuard.js
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import Loadable from 'react-loadable'
import { connect } from 'react-redux'
import renderRoutesMap from './renderRoutesMap'
const mapStateToProps = state => (state)
const mapDispatchToProps = dispatch => ({ ...dispatch })
class RouterGuard extends Component {
constructor(props) {
super()
}
componentWillMount() {
let { history: { replace }, authorization, location } = this.props
if (authorization) replace('./login')
if (location.pathname === '/') replace('./asd')
console.log('路由跳轉前的攔截', this.props)
}
render() {
let { component, routes = [] } = this.props
console.log('準備渲染compoent前', this.props)
const LoadableComponent = Loadable({
loader: () => import(`../${component}`),
loading: () => (
<span>11111</span>
)
})
return (
<div> <LoadableComponent {...this.props} /> {renderRoutesMap(routes)} </div> ) } } export default withRouter(connect(mapStateToProps, mapDispatchToProps)(RouterGuard)) 複製代碼
在這裏,實際上是我項目當中使用的代碼,因此裏面會有react-redux,你們若是不使用redux的話,能夠自行拆解。
componentWillMount是react組件當中的生命週期,在渲染前調用,在這裏,咱們能夠拿到redux當中的參數,也能夠拿到經過路由帶過來的參數。
在這裏,我用的是authorization,若是個人authorization是true,就表明我沒有登陸,跳轉到login登陸頁。
我還作了重定向的操做,好比在根路由的時候,我要重定向跳轉到另一個地址。
其實這樣就能夠實現真正的路由攔截了,可是,這個還不夠,咱們不僅要作攔截,咱們還要作的是,除攔截之外,還要儘量的加快渲染速度,提高用戶體驗。
這裏,我有用到一個Loadable的插件,他的做用就是用於加載具備動態導入的組件的更高階組件,提高用戶體驗。
我在這裏,才用的import,improt是不支持變量的,因此我這裏用的是模版字符串的方式,在每一次進入組件,並準備render的時候,纔去import該組件,這樣,能夠在每一次渲染的時候不用浪費資源,也能夠保證首次渲染的速度變快。
最後,咱們把路由配置、路由數組渲染、路由組件渲染給拼接一下,一整個react-router路由攔截(導航守衛)
//renderRoutes.js
import renderRoutesMap from './renderRoutesMap'
/** * renderRoutes 渲染路由 * @param {array} routes 路由列表 * @param {object} extraProps = {} extra的屬性 * @param {object} switchProps = {} switch的屬性 */
const renderRoutes = ({ routes, extraProps = {}, switchProps = {} }) => (
<Router> <Switch {...switchProps}> {renderRoutesMap(routes)} </Switch> </Router>
)
export default renderRoutes
//index.js
const router = () => (
renderRoutes({
routes: routerConfig
})
)
export default router
複製代碼
最後在頁面當中,引入index.js就能夠了。
這是本人在學習和工做當中使用的方式,若是哪裏有什麼不對了,還但願你們能夠給予指出。最後,但願你們多點贊,多關注,謝謝啦🙏