在咱们本身实现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就能够了。
这是本人在学习和工做当中使用的方式,若是哪里有什么不对了,还但愿你们能够给予指出。最后,但愿你们多点赞,多关注,谢谢啦🙏