react-router-dom v6版本与v5版本对比及v5版本相关替代方案

本文采用尚硅谷2021版React技术全家桶全套完整版中 p77- p93 视频中所使用案例进行演示。该视频中使用的为 V5 版本,所以 V5 版本的代码不会进行过多演示。主要演示V6版本中的替代/解决方案(非实际开发中最优方案,仅仅以视频中的案例为基础对V6版进行演示)。


正文

在React Router v6版本中大量使用了React hooks,因此在尝试使用v6版本之前,需要使用React 16.8或更高版本。——来自React Router AIP

由此可见。我们在接下来的代码中将会使用React hooks相关内容。当然您如果没有接触过相关知识也没有关系,这篇文章的重点并不是React hooks,就算没有了解过也没有太大问题。

OK 接下来我们从

路由的基本使用
开始(仅展示关键代码,完整代码请查看尚硅谷视频)

首先,在App.js中我们定义路由


  {/*
      原生html中,靠标签跳转不同页面
     
About
      Home
  */}
    {/* 在react中靠路由链接实现切换组件 BrowserRouter 已包裹在App外 */}
   About
   Home

注册路由

{/* 注册路由 */}

    } />
    } />

在V6版本中,与V5版本中不一致。V5版本中注册路由,无需包裹组件。而V6版本中则必须包裹。

Routes与Route是基于当前位置渲染内容的主要方法,Route可以想象为if语句,如果URL匹配则呈现元素。

在V6的版本中,Route添加了caseSensitive属性则确定是否以区分大小写的方式进行匹配,该属性默认值为 false。


NavLink的基本使用
我们都知道NavLink在被选中时会被自动添加一个active的class。

在V5版本中,改变NavLink选中样式使用的是activeClassName属性进行替换掉NavLink的active为自定义class,而V6版本中,已将此属性移除。

尚硅谷视频截图NavLink基本使用P79
在V6版本中改变NavLink选中样式可以使用如下方法:
1.使用style属性:

如下在style属性中传递一个回调函数,其回调函数含有一个isActive状态来辨别是否被选中,该值为boolean值。通过该回调函数来指定行内样式(优先级较高)NavLink还是会为我们自动添加 active 样式类。

{
    return isActive ? {
        zIndex: 2,
        color: "#fff",
        backgroundColor: "rgb(247 151 60)",
        borderColor: "rgb(247 151 60)"
    } : null
}} className="list-group-item" to="/about">About

2.直接修改className

    className={({ isActive }) => isActive ? "list-group-item diyActive" : "list-group-item"}
   to="/about"
>Home
同样是通过回调函数中的 isActive 进行判断,然后返回相应样式类名(其中diyActive是我自己定义的一个样式,此处不进行过多展示)。这种方案NavLink则不会自动添加 active 样式类。


Switch组件
在V6版本中Switch组件已移除。在上面文字中也说了使用Routes进行包裹,且是必须的。使用V6版本的Routes进行包裹。则不会出现继续向下匹配组件的问题。且没有V5版本中继续向下匹配路由问题。以及P83中的模糊匹配与严格匹配,在V6版本中exact属性已被移除,所有的路由都是“严格匹配”。


Redirect组件——>Navigate组件
在V6版本中Redirect组件已被移除。在V5版本中使用进行默认路由跳转。而在V6版本中我们可以使用 Navigate 组件来实现默认路由跳转

} />
通过Route组件中的path="*"来对未匹配的链接进行“兜底”,通过Navigate来实现跳转到哪里去。

当然如果你并不想跳转也可以使用下列代码(来自官方API)来自定义未匹配的内容

    path="*"
    element={
        


            

There's nothing here!


        

    }
/>
上面这段代码的意思是,未匹配的内容会在这里输出There's nothing here!这段文字。当然你也可以改成自己喜欢的形状。。

关于Navigate组件:

只要在组件内写了写了Navigate则会一直调用。。除了上面的这种应用方式。感觉其它地方不太好用。如果想要实现点击按钮跳转链接或者定时跳转请使用useNavigate。useNavigate在下面内容中会演示使用方法,请继续往下看。Navigate组件与Link、NavLink的属性差不多。都含有to、state、replace属性。使用起来没什么难度。


嵌套路由
在V5版本的二级路由的注册是直接写在想要展示的位置。而在V6版本中则并不是这样。

尚硅谷视频截图P85嵌套路由(Home路由组件)
在V6版本中我们这样写:

Home组件:

return (


 

我是Home内容


 

   

         

  •         News
         

  •      

  •         Message
         

  •    

   
 


)
App.js:


  } />
  }>
    } />
    } />
    } />
 

  } />

OK。在Home组件中我们可以看到,在里面并没有像截图的红框中直接注册路由。而是写了一个组件。Outlet可以看作一个“占位符”,在父组件所需要渲染子组件的地方进行占位。若父组件中定义的路由完全匹配则会在此处呈现子组件内容,如果没有匹配则不会展现任何内容。而在此处注册路由的工作则统一在App.js中一同管理。

这里需要注意的一点是 在注册路由中,父路由组件的path需要写为"/home/*"末尾一定要添加/*,否则无法实现子组件默认路由选中。

TIP:

MyNavLink是自定义的NavLink组件。代码如下:

import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';

export default class MyNavLink extends Component {
  render() {
    return (
              className={({ isActive }) => isActive ? "list-group-item diyActive" : "list-group-item"}
        {...this.props}
      />
    )
  }
}

向路由组件传递params参数
好的,首先我们来定义Massage组件,在Message组件中向Detail组件转递params参数

import React, { Component } from 'react'
import { Link, Outlet } from 'react-router-dom'

export default class Message extends Component {

  state = {
    messageArr: [
      { id: '01', title: 'message1' },
      { id: '02', title: 'message2' },
      { id: '03', title: 'message3' }
    ]
  }

  render() {
    return (
     


       

            {
              this.state.messageArr.map((messageObj) => {
                return (
                 

  •               {/* 向detail传递参数 */}
                                      to={`/home/message/detail/${messageObj.id}/${messageObj.title}`}>
                      {messageObj.title}
                   
                 

  •               )
              })
            }
           

       

       
     

     )
  }
}
接下来看App.js中注册路由部分:


  } />
  }>
    } />
    } >
      {/* 传递参数 */}
      } />
   

    } />
 

  } />

注册路由部分在path中使用 /:param

前面这些都没有太大变化,重点部分为Message的子组件Detail。

首先我们来看V5版本的处理方式:

尚硅谷视频P86传递Params参数Detail组件
好的接下来看在V6版本中如何处理:

import React, { Component } from 'react';
import { useParams } from 'react-router-dom';

// v6使用class组件。需要封装一下。利用hoc组件来获取参数,然后传递给class组件
function myWithRouter(Detail) {
  return (props) => {
    return
  }
}

const DetailData = [
  { id: '01', content: '11111111111111111111' },
  { id: '02', content: '22222222222222222222' },
  { id: '03', content: '33333333333333333333' }
]

class Detail extends Component {
  render() {
    const contentStr = DetailData.find((detailObj) => {
      return detailObj.id === this.props.params.id;
    })
   return (
   


         
  • ID:{this.props.params.id}

  •      
  • TITLE:{this.props.params.title}

  •      
  • CONTENT:{contentStr.content}

  •      

     )
  }
}

export default myWithRouter(Detail);
这里我们可以看到,上面的Detail组件并没有直接export default进行暴露,而是暴露了myWithRouter(Detail)。这是为什么呢?

原因就是在V6版本中,不能V5版本中那样从this.props中获取路由组件的相关参数了。你如果打印一下props就会发现,props中毛都没有。这里V6版本中就提供了一个hook(钩子),来让我们可以直接获取到所传递的params参数,这个钩子就是 useParams。那么我们为毛不直接使用呢?因为React的hook只能在hoc函数高阶组件中使用。看到这里是不是就明白了为什么要包一层了吧。

上面代码中定义了一个myWithRouter函数组件并将Detail组件传入,在myWithRouter中获取useParams以及相关props,直接传递给Detail组件使用。这样我们就可以在Detail组件中成功的获取到params参数了。

当然上面不是最好的写法。我们直接用函数组件不是更好么。都不需要再进行在外面封装一层了。当然这里对于高阶组件不进行过多探讨,小伙伴们请自己去学习吧。高阶组件目前是React官方推荐的编码方式哦。


向路由组件传递search参数
传递search参数的Message组件与注册路由方式与传递params参数多数代码相似,这里就不过多编写,只展示不同部分

Message组件:

this.state.messageArr.map((messageObj) => {
  return (
   


  •               to={`/home/message/detail/?id=${messageObj.id}&titile=${messageObj.title}`}
          >
            {messageObj.title}
         
       

  •     )
    })
    此处传递参数改为 ?id=xxx&title=xxx 的形式

    App.js正常注册路由:


      } />
      }>
        } />
        } >
          {/* 传递参数 */}
          } />
       

        } />
     

      } />

    Detail接收参数:

    与传递params参数相同,还是需要使用高阶函数组件进行封装。这里V6版本中提供了useSearchParams 钩子。代码如下:

    import React, { Component } from 'react';
    import { useSearchParams } from 'react-router-dom';

    // v6使用class组件。需要封装一下。利用hoc组件来获取参数,然后传递给class组件
    function myWithRouter(Detail) {
      return (props) => {
        // let [searchParams, setSearchParams] = useSearchParams();
        let [searchParams] = useSearchParams();
        const params = {
          id: searchParams.get('id'),
          title: searchParams.get('titile')
        }
        return
      }
    }

    const DetailData = [
      { id: '01', content: '11111111111111111111' },
      { id: '02', content: '22222222222222222222' },
      { id: '03', content: '33333333333333333333' }
    ]

    class Detail extends Component {
      render() {
        const contentStr = DetailData.find((detailObj) => {
          return detailObj.id === this.props.params.id;
        })
       return (
       


           
    • ID:{this.props.params.id}

    •      
    • TITLE:{this.props.params.title}

    •      
    • CONTENT:{contentStr.content}

    •      

         )
      }
    }

    export default myWithRouter(Detail);
    可以看到上面代码中被注释掉的代码let [searchParams, setSearchParams] = useSearchParams();可见useSearchParams不仅提供了获取searchParams参数,还提供了设置searchParams参数。可以通过setSearchParams进行设置。这里就不为大家过多演示。请大家自己尝试吧。


    向路由组件传递state参数
    传递state参数在Message组件中与V5版本稍微有一点区别,不过区别不是太大。

    先看V5版本:

    尚硅谷视频P88传递state参数
    在V6版本中经过我测试,在to中写对象传state的方式并没有用。V6版本中在Link组件(包含NavLink、Navigate)中state作为一个单独的属性与to分开。

    请看下列Message组件关键代码:

    this.state.messageArr.map((messageObj) => {
      return (
       


  •               to='/home/message/detail'
            state={{ id: messageObj.id, title: messageObj.title }}
          >
            {messageObj.title}
         
       

  •     )
    })
    Detail组件同样还是高阶函数进行包裹。使用useLocation钩子进行获取state参数

    import React, { Component } from 'react';
    import { useLocation } from 'react-router-dom';

    // v6使用class组件。需要封装一下。利用hoc组件来获取参数,然后传递给class组件
    function myWithRouter(Detail) {
      return (props) => {
        let location = useLocation();
        /*
          这里写成 location.state || {}
          因为state在刷新后可能为空,所以这里为空时设置为空对象
        */
        const stateObj = location.state || {};
        const params = {
          id: stateObj.id,
          title: stateObj.title
        }
        return
      }
    }

    const DetailData = [
      { id: '01', content: '11111111111111111111' },
      { id: '02', content: '22222222222222222222' },
      { id: '03', content: '33333333333333333333' }
    ]

    class Detail extends Component {
      render() {
        const contentStr = DetailData.find((detailObj) => {
          return detailObj.id === this.props.params.id;
        }) || {}; // 这里 || {} 当未找到匹配的值时contentStr设置为空对象
       return (
       


           
    • ID:{this.props.params.id}

    •      
    • TITLE:{this.props.params.title}

    •      
    • CONTENT:{contentStr.content}

    •      

         )
      }
    }

    export default myWithRouter(Detail);

    编程式路由导航
    上面已经提到过路由组件中的props已经和普通组件一样不会默认传递路由相关参数了。所以还是需要使用高阶组件来实现。

    首先还是来看一下V5版本中Message组件处理方式:

    尚硅谷视频P91编程式路由导航
    从上面图片可以看到实现前进、后退、前进两次,都是使用的this.props.history的方式。

    而在V6版本中则提供了useNavigate来实现前进、后退等操作。下面就来看下Message的代码吧。

    import React, { Component } from 'react'
    import { Link, Outlet, useNavigate } from 'react-router-dom'

    function anonyCom(MessCom) {
      return (props) => {
        let navigate = useNavigate();
        return
      }
    }

    class Message extends Component {

      state = {
        messageArr: [
          { id: '01', title: 'message1' },
          { id: '02', title: 'message2' },
          { id: '03', title: 'message3' }
        ]
      }

      pushShow = (messageObj) => {
        this.props.navigate(
          '/home/message/detail',
          {
            state: {
              id: messageObj.id, title: messageObj.title
            }
        })
      }

      replaceShow = (messageObj) => {
        this.props.navigate('/home/message/detail',
        {
          replace: true,
          state: {
            id: messageObj.id, title: messageObj.title
          }
        })
      }

      goBack = () => {
        this.props.navigate(-1);
      }

      goOne = () => {
        this.props.navigate(1);
      }

      goTwo = () => {
        this.props.navigate(2);
      }

      render() {
        return (
         


           

              {
                this.state.messageArr.map((messageObj) => {
                  return (
                   

    •                                   replace to='/home/message/detail'
                        state={{ id: messageObj.id, title: messageObj.title }}
                     >
                       {messageObj.title}
                     
                     
                     
                   

    •             )
                })
              }
             

           

           
           

           
           
           
         

        )
      }
    }

    export default anonyCom(Message);
    从上面代码中可以看出useNavigate不仅可以传递路由地址,还可以传递数字,传递正数则向前进,传递负数则向后退。并且还可以传递一个对象,对象中包含了replace与state。非常的强大。

    到这里我们可以看到在新版本中,大部分功能都改为了Hook的方式使用。所以如果需要使用新版本则一定要学会Hook相关知识。


    其它
    在V5版本中还有withRouter相关内容。在V6版本中withRouter已经移除。所以在这里就不过多讲解了。若需要实现在头部组件(一般组件)中如何使用路由组件的相关案例。其实我们上面使用高阶函数封装就是一直在写一个自定义的withRouter。。


     

    你可能感兴趣的:(react.js,javascript,前端)