React基础梳理之【路由】

639 阅读6分钟

前言

  • 细阅此文章大概需要 15分钟\color{red}{15分钟}左右
  • 本篇中详细讲述\color{red}{详细讲述}了:
    1. 单页面应用中的路由模式
    2. 多页面应用和单页面应用的对比
    3. Hash和history模式的对比
    4. 路由层面上react和vue的差别
    5. React-router-dom的使用
    6. 路由模式
    7. 路由规则
    8. 单一匹配
    9. 重定向
    10. 路由跳转(标签式)
    11. 路由跳转(编程式路由)
    12. 路由跳转的几种传参方式
    13. withRouter
  • 如果有任何问题都可以留言给我,我看到了就会回复,如果我解决不了也可以一起探讨、学习。如果认为有任何错误都还请您不吝赐教,帮我指正,在下万分感谢。希望今后能和大家共同学习、进步。
  • 下一篇会尽快更新,已经写好的文章也会在今后随着理解加深或者加入一些图解而断断续续的进行修改。
  • 如果觉得这篇文章对您有帮助,还请点个赞支持一下,谢谢大家!

概要

  • 【在整个react中为什么会有dom?】
    • 比如react-domreact-router-dom,是因为除了 react作为核心 外,还有 react-dom支持h5页面开发 ,以及 react-native支持App的开发
    • 而如果vue框架想要实现App的开发,就需要配合uni-app来使用
  • 由于react等前端框架都是基于单页面应用模式来设计的,以实现页面的局部刷新,所以页面的路由模式都是基于(hash或history)两种模式来实现的。

多页面应用和单页面应用的对比

  • SPA单页面WEB应用(single page web application)
    • (一个产品只有一个页面,所有内容的展示和业务的处理都是在这个页面完成的)
  • MPA多页面WEB应用(multi page web application)
    • (一个产品包含很多页面,通过页面之前的跳转完成业务逻辑的衔接等)
多页面应用模式(MPA)单页面应用模式(SPA)
应用组成多个完整页面组成一个外壳页面多个页面片段构成
跳转方式页面之间的跳转是从一个页面跳转到另个页面页面片段之间的跳转是把个页面片段删除或隐藏,加载另一个页面片段并显示出来。这是片段之间的模拟跳转,并没有离开壳页面
刷新方式整页刷新页面片段局部刷新
跳转后公共资源是否重新加载
url模式http://xxx.page1.html http://xxx.page2.htmlhttp://xxx.shell.html#page1 http://xxx.shell.html#page2
用户体验页面间切换加载慢,不流畅,用户体验差,特别是在移动设备上页面片段间的切换快,用户体验好,包括在移动设备上
能否实现转场动画无法实现容易实现
页面间数据传递依赖URL、cookie、或者localStorage,实现麻烦因为在一个页面内,页面片段间传递数据很容易实现
搜索引擎优化(SEO)可以直接做需要单独方案做,有些麻烦
特别适用范围需要对搜索引擎友好的网站对体验要求高的应用,尤其是移动应用
开发难度低一些,框架选择容易高一些,需要专门的框架来降低这种模式的开发难度

结论 :单页应用模式由于有很多好处,已经是web应用开发的潮流,特别是移动应用开发


hashhistory模式的对比

hash模式

  • hash模式是通过#在url后拼接参数,再根据规则的不同来渲染不同的组件,此种模式的路由跳转无需服务器的支持
  • 【缺点】:url太丑

history模式

  • 没有#号,地址栏干净,但后面拼接的地址不是真实地址,需要服务器的支持

react中的路由

路由层面上react和vue的差别

  • react中的路由机制与vue,思想上相同,语法上不同
    1. 首先react中的路由没有路由表想要在哪个地方渲染不同组件,就要在视图当中的那个地方配置好其路由,一级路由二级路由...都是如此 ,都要在视图当中指定位置进行判断、渲染。并没有单独设置路由表的形式
    2. 路由规则匹配机制不同在react中,并不是某一个路由匹配成功了就不再向下找了,而是接着向下找,如果依然成功就接着找。 而vue中则是,若匹配到一个成功的,就不再向下继续匹配了,所以为了避免渲染多个页面出来,要用<Switch>标签将所有<Route>路由规则包裹起来,以实现匹配到一个就不再继续匹配的效果
    3. react中路由并没有实现路由懒加载

使用

安装

  • 想要在react中使用路由,则需要先安装react-router-dom $yarn add react-router-dom

引入

  • 接着想要在组件中使用,就要先引入,并且在引入的过程中,解构出所需的功能 import {} from 'react-router-dom'
    1. HashRouter hash路由模式
    2. BrowserRouter history路由模式
    3. Route 路由规则,根据不同的地址跳转不同的组件
    4. Switch 实现路由单一匹配的效果(若成功匹配路由,就不再继续向下匹配了)
    5. Redirect 重定向

使用

路由模式(HashRouter/BrowserRouter)
  • 用hash模式举例,若我要使用hash模式,则就在引入之后,在页面入口文件中,用<HashRouter></HashRouter>将所有结构包裹起来

路由规则(Router)
  • 若想根据不同的规则渲染不同的组件,就需要使用<Route>来配置路由规则
  • Route当中有一些属性,来配置此路由规则
    1. path 匹配地址,但不是 精准匹配
      • 若【 path='/' ,则 /、/home、/system、/home/xxx 都会被匹配为/
      • 若【 path='/home' ,则 /home、/home/xxx 都会被匹配为/home
    2. exact 精准匹配path(对于匹配度较高的path,如/,最好还是加上exact来进行精准匹配)
      • 所以在如/等一级路由上,需要使用精准匹配;而/home则无需使用精准匹配,因为其底下的二级路由也要由此进入;(若只有一级,则看情况使用)
    3. component : 对应要渲染的组件
    4. render : 渲染函数(可看作是导航守卫来使用)
      • 在react路由中,并没有vue一样的路由守卫,所以如果想在路由跳转时判断一些条件:如权限,就可以在render函数中进行判断,根据判断来决定是否渲染某组件

单一匹配(Switch)
  • 在react中,并不是某一个路由匹配成功了就不再向下找了,而是接着向下找,如果依然成功就接着找。 所以为了避免渲染多个页面出来,要用<Switch>标签将所有<Route>路由规则包裹起来,以实现匹配到一个就不再继续匹配的效果

重定向(Redirect)
  • 若所有的路由规则都不匹配,那么就重定向到某个组件,比如说首页;使用<Redirect />
  • Redirect当中有一些属性,来配置重定向
    1. path : 要匹配的地址,不使用此属性则为Route没匹配到任何项时
    2. to : 配置重定向跳转的地址
    3. exact : 配置精准匹配

跳转(Navlink)【标签跳转】
  • 编译完成的结果还是a标签
  • Navlink当中有一些属性,来配置跳转
    1. to : 用来配置跳转地址,会拿要跳转的地址和路由地址进行匹配,若匹配上,则跳转的同时,会给当前标签加一个active样式类,让当前标签存在选中样式 【与link的区别,就是是否有active样式】
      • to属性可以是一个地址也可以是一个对象 ,对象当中可以配置三个参数
        1. pathname :地址
        2. search?xxx=xxx 问号传参的值
        3. hash:hash传值(一般用不到)
    // 导航组件使用NavLink设置跳转
    import { NavLink } from 'react-router-dom'
    const Nav = function Nav(){
        return (
            <section className='nav_box'>
                <h2>OA管理系统</h2>
                <nav>
                    <NavLink to='/home'>首页</NavLink>
                    <NavLink 
                        to={{
                            pathname='/system',
                            search='?xxx=xxx&xxx=xxx',
                            hash='123123'
                        }}
                    >系统设置</NavLink>
                </nav>
            </section>
        )
    }
    export defaule Nav;

跳转(link)【标签跳转】
  • 与NavLink用法一致,只是没有active样式
  • 具体使用见二级路由

编程式跳转【编程式路由】
  • 所有经过Route路由path规则匹配之后渲染出来的组件都是受路由管控组件
  • 所有受路由管控的组件,都会默认在props中接收到几个参数
    1. history : 提供跳转的方法 ,如 pushgogoBack ....
    2. location : 拿到地址匹配的信息 ,如 pathnamesearch问号传参参数hash
    3. match : 拿到地址匹配的规则 , 如 isExact(是否为精确匹配)params(匹配规则的path和当前地址)

路由跳转传参的方式
  1. 问号传参:
    • history.push('/custom/detail?id=${id}');
    • 跳转到目标页面后(/custom/detail),通过this.props.location.search获取(注意函数组件和类组件的使用区别)
  2. 路径传参(这种方式需要路由配置参与):
    • history.push('/custom/detail/${id}');
    • <Route path='/custom/detail/:id?' component={CustomDetail} />
    • 跳转到目标页面后(/custom/detail),通过this.props.match.params获取(注意函数组件和类组件的使用区别)
  3. Link传参(隐式): 这种方式地址栏中不显示参数信息
        {/* link方式 */}
        <Link 
            to={{
                pathname:'/custom/detail',
                state:{id:id}
            }}
        >
        客户列表
        </Link>
        {/* 编程式路由跳转方式 */}
        props.history.push({
          pathname:'/home/customadd',
          state:{
              id:100
          }
        })
    
    • 标签方式编程式导航方式
    • 跳转到目标页面后(/custom/detail),通过this.props.location.state获取(注意函数组件和类组件的使用区别)
    • 弊端:一旦页面刷新,信息就丢失了,无法基于state获取
    • 场景:多用于跳转进来填表单,保证一定是从某处跳转而来;保证有些信息不在地址栏显示,安全性提高一些
  • 传递参数
   // CustomList组件是基于Route匹配渲染的受路由管控组件
    const CustomList = function CustomList(props){
        const onClick = ()=>{
            /* 路径传参,需要配合Route配置 */
            // props.history.push(`/home/customadd/${100}`);
            /* 问号传参 */
            // props.history.push({
            //     pathname:'/home/customadd',
            //     search:'?id=100'
            // })
            /* 隐式传参 */
             props.history.push({
                pathname:'/home/customadd',
                state:{
                    id:100
                }
            })
        }
        return (
            <div>
                客户列表
                <button onClick> 编辑</button>
            </div>
        )
    }
    export defaule CustomList; 
  • 接收参数
   // CustomAdd中接收路由传递的参数
    const CustomAdd = function CustomAdd(props){
        // 接收路径传参
        console.log('获取路径传参传递的值')
        console.log(props.match.params)
        // 接收问号传参
        console.log('获取问号传参传递的值')
        console.log(props.location.search)
        // 接收隐式传参
        console.log('获取问号传参传递的值')
        console.log('当页面刷新,参数丢失')
        console.log(props.location.state)
        return (
            <div>
                新增客户
            </div>
        )
    }
    export defaule CustomAdd; 

把不受路由管控组件变为受路由管控组件(withRouter)
  • 目的:在其props中可以使用history、location、match
    // withRouter
    import { NavLink,withRouter } from 'react-router-dom'
    const Nav = function Nav(props){
        console.log(props.history)
        console.log(props.location)
        console.log(props.match)
        return (
            <section className='nav_box'>
                <h2>OA管理系统</h2>
                <nav>
                    <NavLink to='/home'>首页</NavLink>
                    <NavLink 
                        to={{
                            pathname='/system',
                            search='?xxx=xxx&xxx=xxx',
                            hash='123123'
                        }}
                    >系统设置</NavLink>
                </nav>
            </section>
        )
    }
    export defaule withRouter(Nav);

一级路由

    // 一级路由
    // 使用hash路由,配置route路由规则(exact精准匹配),使用Switch来单一匹配,使用Redirect来重定向
    import { HashRouter, Route, Redirect, Switch } from 'react-router-dom';

    const App = function App(){
        return(
            <HashRouter>
                <Nav />
                <section className='content'>
                    {/*<System />*/}
                    {/*<Home />*/}
                    <Switch>
                        {/* 一般不会出现【两个地址渲染同一个组件】的情况,所以如果确实两个地址要匹配同一个组件,就要使用重定向的形式 */}
                        {/* <Route path='/' component={Home}/> */}
                        <Redirect path='/' exact to='/home'/>
                        <Route path='/home' component={Home}/>
                        {/* 路由中的权限校验,render函数 */}
                        <Route path='/person' component={Person} render={()=>{
                            if(isPm){
                                return <Person />
                            }
                            return <Redirect to='/login'/>
                        }}/>
                        <Route path='/system' component={System}/>
                        {/* 最后全部没有匹配到的重定向要写在最后 */}
                        <Redirect to='/home'/>
                    </Switch>
                    
                </section>
            </HashRouter>
        )
    }
    export default App;

二级路由

  • 在一级路由中配置过路由模式了,二级路由中就不用再配了
  • 依然需要Switch
    // 二级路由
    import { Route, Redirect, Switch, Link } from 'react-router-dom';
    const Home = function Home(){
        return(
            <>
                <section className='menu_Box'>
                    {/* Link跳转 */}
                    <Link 
                        to={{
                            pathname:'/home/customlist',
                            search:'?lx=all'
                        }}
                    >客户列表</Link>
                    <Link to='/home/customadd'>新增客户</Link>
                </section>
                <section className='form_Box'>
                    <Switch>
                        {/* 一般不会出现【两个地址渲染同一个组件】的情况,所以如果确实两个地址要匹配同一个组件,就要使用重定向的形式 */}
                        <Redirect path='/home' exact to='/home/customlist'/>
                        <Route path='/home/customlist' component={CustomList}/>
                        <Route path='/home/customadd' exect component={CustomAdd}/>
                        {/* 若要在customlist组件跳转customadd组件时使用路径传参,就需要在这里route进行配置,而同样不需要配置的route也保留,但要加exect,避免提前匹配导致拿不到参数 */}
                        <Route path='/home/customadd/:id?' component={CustomAdd}/>
                        {/* 最后全部没有匹配到的重定向要写在最后 */}
                        <Redirect to='/home/customlist'/>
                    </Switch>
                </section>
            </>
        )
    }
    export default Home;