写点什么

React 进阶(五):导航守卫

  • 2021 年 12 月 15 日
  • 本文字数:3080 字

    阅读完需:约 10 分钟

React进阶(五):导航守卫

一、前言

在《React进阶(四):路由介绍》博文中,介绍了React路由相关知识,在实际项目开发过程中,路由之间的跳转必定涉及权限、用户是否登陆等限定条件的判定,故需要导航守卫来完成这一事项。


在实现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是灵活的,不希望在里面添加太多的api,这些api应该是让使用者根据自己的需求去实现路由拦截。下面,就开始实现一个可以满足大部分需求的路由拦截。


react-router版本:4.0


首先,要使用react-router-config,用数组的形式去写一个路由配置:


//routerConfig.jsconst 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.jsimport 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.jsimport 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))
复制代码


componentWillMountreact组件当中的生命周期,在渲染前调用,在这里,我们可以拿到redux当中的参数,也可以拿到通过路由带过来的参数。


在这里,用的是 authorization,如果 authorization 是 true,就代表我没有登录,跳转到 login 登录页。


此外,还做了重定向的操作,比如在根路由的时候,要重定向跳转到另外一个地址。


其实这样就可以实现真正的路由拦截了,但是,这个还不够,我们不只要做拦截,我们还要做的是,除拦截以外,还要尽可能的加快渲染速度,提升用户体验。


这里,我有用到一个react-loadable路由懒加载的组件,作用就是路由懒加载,加载具有动态导入组件的更高阶组件,提升用户体验。


这里用到的import是不支持变量的,所以这里用的是模版字符串的方式,在每一次进入组件,并准备render的时候,才去import该组件,这样,可以在每一次渲染的时候不用浪费资源,也可以保证首次渲染的速度变快。


最后,把路由配置、路由数组渲染、路由组件渲染给拼接一下,一整个react-router路由拦截(导航守卫)


//renderRoutes.jsimport 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.jsconst router = () => (    renderRoutes({        routes: routerConfig    }))export default router
复制代码


最后在页面当中,引入index.js就可以了。

三、拓展阅读

发布于: 2 小时前阅读数: 5
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
React进阶(五):导航守卫