React - router的使用 && 结合react-redux的路由守卫

  • web端使用路由安装的是 react-router-dom

    "react-router-dom": "^5.2.0"

  • 在组件中使用路由,我们先设置2个路由,分别是首页、关于

    // src/components/RouteSample.js

    import React from 'react';
    // 引入路由需要的基础模块
    import {BrowserRouter, Link, Route} from 'react-router-dom'

    export default function RouteSample () {
    return (


    <BrowserRouter>

            {/* 导航链接 */}
            <div><Link to="/">首页</Link></div>
            <div><Link to="/about">关于</Link></div>
    
            {/* 路由配置:路由就是组件 */}
            <Route path="/" component={Home}></Route>
            <Route path="/about" component={About}></Route>
    
          </div>
        </BrowserRouter>
      </div>
    )
    

    }

  • 然后补充首页、关于2个组件,这时页面就可以切换路由了,但是发现一个问题,当路由切换至 about 时,首页的内容依然展示在页面上,这是 react 一个特点叫包容式路由,默认情况是多个组件组成一个页面展示,也就是当我们访问 /about 时会包含 / 的内容
    "关于"页面.jpg

    // src/components/RouteSample.js

    function Home() {
    return


    Home


    }

    function About () {
    return (


    About


    )
    }

  • 接着上面的情况想要改成独占式的路由,也就是一个路由只展示当前组件,我们用到一个属性 exact

    {/* 路由配置:路由就是组件 */}
    <Route exact path="/" component={Home}></Route>
    <Route path="/about" component={About}></Route>

  • react 路由也是有动态路由概念的,我们在首页展示一个课程列表,点击分别进入对应的课程详情页,继续在上面的代码基础上扩展

    // src/components/RouteSample.js

    function Home() {
    return


    Home


    • <Link to="/detail/web">web</Link>

    • <Link to="/detail/ios">ios</Link>



    }

    // src/components/RouteSample.js

    {/* 新增详情页路由 lesson就是动态传参的部分*/}
    <Route path="/detail/:lesson" component={Detail}></Route>

    // src/components/RouteSample.js

    // 新增详情页组件

    // 传递进来的是路由器对象
    // 路由器对象主要包含以下3部分,可以结合下面的具体截图理解
    // 1. history 导航指令 history.goBack
    // 2. match 获取参数信息
    // 3. location 当前url信息
    function Detail (routeParams) {
    console.log('routeParams',routeParams)
    return (


    当前课程:{routeParams.match.params.lesson}
    <button onClick={routeParams.history.goBack}>后退</button>

    )
    }

路由器参数.jpg

  • 在开发过程中有时候会碰到所有路由都不匹配的情况,也就是路径不对、页面找不到,通常会给一个404页面,在 react 中这样处理,加上404路由之后会发现无论路由在哪个页面404页面会一直显示(如下图),这里又遇到上面同样的问题 react 路由匹配时包容性质的,这里用到一个新组件 Switch ,不用像之前给每个组件添加 exact 属性那么麻烦,路由只会在 Switch 包裹范围内选1个,修改完之后会发现只有在路由都不匹配的情况下才会显示404页面

    // src/components/RouteSample.js

    // 新增404页面组件 展示访问的具体路径不存在
    function NoMatch ({location}) {
    return (

    404, {location.pathname}不存在

    )
    }

    // 新增404路由
    {/* 404页面没有path 当有path的路由都不匹配时那必然匹配404*/}
    <Route component={NoMatch}></Route>

首页展示404.jpg

ios详情页显示404.jpg

// src/components/RouteSample.js

import React from 'react';
// 引入Switch
import {BrowserRouter, Link, Route, Switch} from 'react-router-dom'

{/* 路由配置:路由就是组件 */}
{/* 用Switch包裹路由配置,避免展示包容式路由 */}
<Switch>
  <Route exact path="/" component={Home}></Route>
  <Route path="/about" component={About}></Route>
  <Route path="/detail/:lesson" component={Detail}></Route>
  {/* 404页面没有path 当有path的路由都不匹配时那必然匹配404*/}
  <Route component={NoMatch}></Route>
 </Switch>

路由不存在显示404.jpg

  • 接着再研究下路由嵌套,修改一下 About 组件,用作展示当前用户信息,分为"个人信息"和"订单信息",按原先的路由结构来看,现在"个人信息"、"订单信息"就是嵌套在 About 下的,从页面上可以看出这里有一个问题,就是点击"关于"展示 About 时,没有给嵌套路由默认展示项,必须要手动再次点击"个人信息"或者"订单信息"才能正常显示(默认情况如下图),这时候可以给一个路由重定向作为默认展示的路由,结合 vue 还是很容易理解的

    // src/components/RouteSample.js

    // 当前用户信息
    function About () {
    return (


    About


    <Link to="/about/me">个人信息</Link>
    <Link to="/about/order">订单信息</Link>

    <Switch>
    <Route path="/about/me" component={() => (
    me
    )}></Route>
    <Route path="/about/order" component={() => (
    order
    )}></Route>
    </Switch>

    )
    }

about默认不展示嵌套路由内容.jpg

// src/components/RouteSample.js

import {BrowserRouter, Link, Route, Switch, Redirect} from 'react-router-dom'

<Switch>
  <Route path="/about/me" component={() => (<div>me</div>)}></Route>
  <Route path="/about/order" component={() => (<div>order</div>)}></Route>
  {/* 路由默认项 */}
  <Redirect to="/about/me"></Redirect>
</Switch>
  • 路由中有一项非常重要的功能是路由守卫,可以用于很多场景,最常见的就是以是否登录判断显示当前访问页面还是跳转登录页,react 中的路由守卫跟 vue-router 的有很大不同,react 是用一个高阶组件扩展 <Route>

    // src/components/RouteSample.js

    // 保留路由组件所需的参数component、以及其他参数例如pth等,同时传一个登录状态isLogin
    function PrivateRoute ({component: Comp, isLogin, ...rest}) {
    return (
    // render函数 根据条件动态渲染组件
    // 未登录则重定向至登录页
    // 登录路由也配置了一个redirect,这样在设计登录组件的时候当登录成功即重定向跳转至登录前访问的页面
    <Route
    {...rest}
    render = {
    props => isLogin ? (<Comp></Comp>) : (<Redirect
    to={{pathname: "/login", state: {redirect: props.location.pathname}}}
    ></Redirect>)
    }
    ></Route>
    )
    }

  • 然后补充登录组件和路由配置,同时修改 About 组件的使用

    // src/components/RouteSample.js

    // 从路由参数拿到重定向地址,从redux获取登录状态和登录方法
    function Login ({location, isLogin, login}) {
    const redirect = location.state.redirect || '/'
    if(isLogin) {
    return <Redirect to={redirect}></Redirect>
    }
    return (


    用户登录




    <button onClick={login}>login</button>

    )
    }

    {/* 路由配置:路由就是组件 /}
    <Switch>
    <Route exact path="/" component={Home}></Route>
    {/
    使用路由守卫 /}
    <PrivateRoute path="/about" component={About}></PrivateRoute>
    <Route path="/detail/:lesson" component={Detail}></Route>
    <Route path="/login" component={Login}></Route>
    {/
    404页面没有path 当有path的路由都不匹配时那必然匹配404*/}
    <Route component={NoMatch}></Route>
    </Switch>

  • 上一步已经实现了路由守卫的逻辑,下一步则补充 redux 中关于登录状态以及登录方法异步请求的相关内容,新建 redux 中的用户模块,路径是 src/store/user.redux.js,用户模块添加完成后要在 src/store/index.js 中完成注册才能使用

    // src/store/user.redux.js

    // 初始化状态,没在登录中 并且 未登录
    const initial = {
    isLogin: false,
    loading: false
    }

    export const user = (state = initial, action) => {
    switch(action.type) {
    // 登录中
    case "requestLogin":
    return {
    isLogin: false,
    loading: true
    };
    // 已登录
    case "login":
    return {
    isLogin: true,
    loading: false
    };
    default:
    return state;
    }
    }

    // 请求登录异步调用时,先修改状态为登录中
    export const login = () => dispatch => {
    dispatch({type: 'requestLogin'});
    setTimeout(() => {
    dispatch({type: 'login'})
    }, 1500);
    }

    // src/store/index.js

    // applyMiddleware 应用中间件方法
    import {createStore, applyMiddleware, combineReducers} from 'redux';
    import logger from 'redux-logger'
    import thunk from 'redux-thunk'
    import {user} from './user.redux'

    const store = createStore(
    combineReducers({user}),
    applyMiddleware(logger, thunk)
    );

    export default store;

  • 完成以上模块添加之后再回到路由守卫的部分做对应修改,路由守卫和登录组件都需要用到登录状态值,这里我们使用 react-redux

    // src/components/RouteSample.js

    // 添加引用
    import {connect} from 'react-redux';
    import {login} from '../store/user.redux'

    // 路由守卫
    const PrivateRoute = connect(
    state => ({isLogin: state.user.isLogin})
    )(
    ({component: Comp, isLogin, ...rest}) => {
    return (
    // render 根据条件动态渲染组件
    <Route
    {...rest}
    render={
    props => isLogin ?
    (<Comp></Comp>) :
    (<Redirect
    to={{pathname: "/login", state: {redirect: props.location.pathname}}}
    ></Redirect>)
    }
    >
    </Route>
    )
    }
    )

    // 登录组件
    const Login = connect(
    // 映射状态与dispatch方法
    state => ({
    isLogin: state.user.isLogin,
    loading: state.user.loading
    }),
    {login}
    )(
    ({location, isLogin, login, loading}) => {
    const redirect = location.state.redirect || '/'
    if(isLogin) {
    return <Redirect to={redirect}></Redirect>
    }
    return (


    用户登录




    {/* loading优化 */}
    <button onClick={login} disabled={loading}>
    {loading ? 'login...' : 'login'}
    </button>

    )
    }
    )

  • 最后需要注意的是使用路由组件时使用方式、引入 store 的方式

    // App.js

    import store from './store/index'
    import {Provider} from 'react-redux'

    import RouteSample from './components/RouteSample'

    {/* router */}
    <Provider store={store}>
    <RouteSample></RouteSample>
    </Provider>

  • 下图展示了整个登录流程发生的记录

登录流程.jpg
© 著作权归作者所有,转载或内容合作请联系作者

喜欢的朋友记得点赞、收藏、关注哦!!!

相关推荐
kingbal2 小时前
SpringCloud:gateway分发服务报302,Network Error
java·spring cloud·gateway
AskHarries4 小时前
Spring Cloud 3.x 集成 BigQuery
java·后端·spring cloud
命运之手5 小时前
[ Spring ] Spring Boot Live Reload
spring boot·spring·live-reload
借来一夜星光5 小时前
【前端动效】HTML + CSS 实现打字机效果
前端·css3·html5
借来一夜星光6 小时前
【前端动效】原生js实现拖拽排课效果
前端·javascript·css3·html5
工业互联网专业6 小时前
基于springboot+vue的高校网上缴费综合业务系统
java·vue.js·spring boot·毕业设计·源码·课程设计
iiaythi6 小时前
spring boot 支持jsonp请求
spring boot
许苑向上6 小时前
Spring中三级缓存详细讲解
java·spring·三级缓存机制
大强的博客7 小时前
《Spring Framework实战》11:4.1.4.2.详细的依赖和配置2
java·后端·spring
ldj20207 小时前
SpringBoot错误码国际化
spring boot·国际化