实现一个react-router路由拦截(导航守卫)
在我们自己实现react-router的路由拦截之前,我们先看一下vue的路由拦截是怎么使用的,都做到了哪些事情:
正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。
全局守卫
你可以使用 router.beforeEach 注册一个全局前置守卫:
const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { // ...
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
在这里,我们可以看到,vue在所有的路由跳转前,在beforeEach中可以监听所有的路由跳转,如果符合规则,就进行跳转,如果不符合,那么就跳转到我指定的位置。
react-router当中,为什么不能提供同样的api呢?作者在github中回复到:
You can do this from within your render function. JSX doesn't need an API for this because it's more flexible.
大概的意思就是就是:
您可以在渲染功能中执行此操作。JSX不需要API,因为它更灵活。
链接:react-router路由拦截官方说明(https://github.com/ReactTraining/react-router/issues/4962)
在作者的回复当中可以看到,他希望react-router是灵活的,不希望在里面添加太多的api,这些api应该是让使用者,根据自己的需求去实现自己的路由拦截。下面,就开始实现一个自己的,可以满足大部分需求的路由拦截。
react-router版本:4.0
首先,我们要使用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就可以了。
这是本人在学习和工作当中使用的方式,如果哪里有什么不对了,还希望大家可以给予指出。最后,希望大家多点赞,多关注,谢谢
更多相关技术文章,请访问HTML中文网!
以上就是react怎么实现路由拦截的详细内容,更多请关注易知道|edz.cc其它相关文章!