如何實現一個react-router路由攔截(導航守衛)

在咱們本身實現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就能夠了。

這是本人在學習和工做當中使用的方式,若是哪裏有什麼不對了,還但願你們能夠給予指出。最後,但願你們多點贊,多關注,謝謝啦🙏

相關文章
相關標籤/搜索