【学习记录】React-Router @V5 V6的基本使用

846 阅读2分钟

一、React-Router @V5

快速开始

安装

npm i react-router-dom@5

示例:基本路由

import React from "react"
import { BrowserRouter as Router, Route, Link } from "react-router-dom"

function Index(){
  return <h2>Home</h2>
}

function About(){
  return <h2>About</h2>
}

function Users(){
  return <h2>Users</h2>
}

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to='/'>Home</Link>
            </li>
            <li>
              <Link to='/about'>About</Link>
            </li>
            <li>
              <Link to='/users'>Users</Link>
            </li>
          </ul>
        </nav>

        <Route path='/' exact component={Index}></Route>
        <Route path='/about/' component={About}></Route>
        <Route path='/users/' component={Users}></Route>
      </div>
    </Router>
  )
}

示例:嵌套路由

这个示例向我们展示来嵌套路由如何工作,路由 '/topics'会加载Topics组件,这个组件会通过':id'的路由来渲染出更多的内容。

import React from 'react'
import {
    BrowserRouter as Router,
    Route,
    Link
} from "react-router-dom"

function Home(){
    return <h2>Home</h2>
}

function About(){
    return <h2>About</h2>
}

function Topic({match}){
    return <h3>Requested Param:{match.params.id}</h3>
}


function Topics({ match }) {
    return (
        <div>
            <h2>Topics</h2>

            <ul>
                <li>
                    <Link to={`${match.url}/components`}>Components</Link>
                </li>
                <li>
                    <Link to={`${match.url}/props-v-state`}>Props v. State</Link>
                </li>
            </ul>

            <Route path={`${match.path}/:id`} component={Topic} />
            <Route
                exact
                path={match.path}
                render={() => <h3>Please select a topic.</h3>}
            />
        </div>
    );
}


function Header() {
    return (
        <ul>
            <li>
                <Link to="/">Home</Link>
            </li>
            <li>
                <Link to="/about">About</Link>
            </li>
            <li>
                <Link to="/topics">Topics</Link>
            </li>
        </ul>
    );
}

export default function App() {
  return (
    <Router>
        <div>
            <Header/>

            <Route exact path='/' component={Home}/>
            <Route  path='/about' component={About} />
            <Route  path='/topics' component={Topics} />
        </div>
    </Router>
  )
}

基本组件

React Router包含三种类型的组件:路由组件、路由匹配组件、导航组件

在你使用这些组件之前,必须先导入他们

import { BrowserRouter, Route, Link } from "react-router-dom"

路由组件

任何一个拥有路由跳转的React应用的核心组件必须是一个路由组件。对于web项目来说,react-router-dom提供了两种路由组件:BrowserRouterHashRouter,他们会为你创建一个history对象,通常来说,对于一个有服务器响应的web项目,那就使用BrowserRouter,如果你是用静态资源来提供服务的,那就使用HashRouter

import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  holder
);

路由匹配组件

路由匹配组件有两种:<Route><Switch>

路由匹配组件通过匹配Routepath属性与当前页面的地址栏的路径来工作。当一个<Route>匹配成功时,她会渲染出对应的内容,当匹配不成功的时候,任何内容都不会被渲染出来。

当一个<Route>没有path属性时她对任何路径都会匹配成功。

你可以在你想要根据浏览器地址来渲染内容的任何地方使用<Route>,但是我们通常会把一组<Route>放在一起。<Switch>就是用来把多个<Route>组合在一起的。你可以在你想要根据浏览器地址来渲染内容的任何地方使用<Route>,但是我们通常会把一组<Route>放在一起。<Switch>就是用来把多个<Route>组合在一起的。

我们不是必须要用<Switch>把多个<Route>组合在一起,但是这种做法通常是有用的。 <Switch>将迭代其所有子<Route>元素,并仅渲染与当前路径匹配的第一个子元素。 她对于多个path匹配相同的路径、动画路由之间的转换、没有路径匹配时的识别(这样你就可以渲染“404”组件)是有很大帮助的。

import { Route, Switch } from "react-router-dom";
// when location = { pathname: '/about' }
<Route path='/about' component={About}/> // renders <About/>
<Route path='/contact' component={Contact}/> // renders null
<Route component={Always}/> // renders <Always/>

<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/about" component={About} />
  <Route path="/contact" component={Contact} />
</Switch>

<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/about" component={About} />
  <Route path="/contact" component={Contact} />
  {/* when none of the above match, <NoMatch> will be rendered */}
  <Route component={NoMatch} />
</Switch>

路由渲染属性

对于一个<Route>组件,你可以设置三种属性:component, render, children 来渲染出相应的内容。在这里我们只关注component和render,因为她们是经常会用到的

当你有一个已存在的组件(无论是一个React组件还是一个无状态的函数组件)想要渲染时应该使用component。当你必须传递一些参数变量给组件时应该用render属性,她采用内联函数的形式。你不应该使用component属性来渲染一个带有参数变量的内联函数组件,这会导致不必要的组件的挂载和卸载。

component的属性值不能为一个内嵌函数

<Route path="/somewhere" component={() => <Home />} /> // 错误的使用 
<Route path="/somewhere" component={Home} /> // 正确使用

当使用component属性时,Route会用React.createElement创建一个子组件,这意味着给component传递一个内嵌函数时,Route会创建一个新组件,原来的组件会被卸载,然后挂载新组件,这样会有性能消耗。

想要给子组件传递一些额外的参数,请使用render

export default function App() {
  return (
    <Router>
        <Switch>
              {/* these are good */}
              <Route exact path="/" component={Home} />
              <Route
                  path="/about"
                  render={props => <About {...props} extra={someVariable}/>}
              />
              {/* do not do this */}
              <Route
                  path="/contact"
                  component={props => <Contact {...props} extra={someVariable}/>}
              />
        </Switch>
    </Router>
  )
}

导航组件

React Router提供了<Link>组件用来在你的应用中创建超链接,<Link>会在你的页面的任何地方被渲染成a标签。

<NavLink>是一种特殊的<Link>,当他的to属性匹配地址栏上的路径时,她渲染成的<a>标签会带有'active'的样式。

如果你想要强制跳转,你可以使用<Redirect>。当一个<Redirect>组件被渲染时,她会导航到其to属性匹配的路径。

<Link to="/">Home</Link>
// <a href='/'>Home</a>

// location = { pathname: '/react' }
<NavLink to="/react" activeClassName="hurray">
  React
</NavLink>
// <a href='/react' className='hurray'>React</a>

<Redirect to="/login" />

路由的严格匹配与模糊匹配

  • 默认使用的时模糊匹配(Route的path属性值是地址栏上的一部分,且顺序要一致)
  • 开启严格匹配:<Route exact path="/about" component={About} />
  • 不要随便开启严格匹配,需要时再开,有些时候开启会导致无法继续匹配二级路由

路由传参的三种方式

方式一:向路由组件传递params参数

1. 在导航组件中将params参数拼接在路径后

<Link to={`/home/message/detail/${id}/${title}`}>点击</Link>

2.在路由匹配组件中声明接收 路径/:id/:title

<Route path='/home/message/detail/:id/:title' component={Detail}></Route>

3.在路由组件中propsmatch属性里拿到params参数

const [id,title] = props.match.params

image.png

注意:传递params参数时,一定要在Route的path路径后声明接收,否则在第三步拿不到params值

方式二:传递search参数

1.在路径后拼接query参数

<Link to={`/home/message/detail?id=${id}&title=${title}`}>点我</Link>

2.在路由组件的props里的location对象中接收

image.png

方式三:传递state参数

1.在路由链接中加上state属性

<Link to={{ pathname:'/home/message/detail', state:{id, title}}}>点我</Link>

2. 在路由组件中接收

const [id,title] = props.location.state

image.png

总结三种传参方式

三种传参方式.png

编程式路由导航

props.history.push(path, state) //保存历史记录

props.history.replace(path, state) //替换历史记录

props.history.goForward() //前进一步

props.history.goBack() //后退一步

props.history.go(steps) // steps>0 前进steps步 steps<0 后退steps步

withRouter

withRouter可以加工一般组件,让一般组件具备路由组件所特有的API,向一般组件内传入以下属性

image.png

BrowserRouter与HashRouter的区别

  • 底层原理不一样:

    BrowserRouter使用的是H5的history API,不兼容IE9及以下版本

    HashRouter使用的是URL的哈希值

  • url表现形式不一样

    BrowserRouter的路径中没有#,HashRouter的路径中包含#

  • 刷新后对路由state参数的影响

    BrowserRouter没有任何影响,因为state保存在history对象中

    HashRouter刷新后会导致路由state参数的丢失

  • 备注:HashRouter可以用于解决一些路径错误相关的问题

二、React Router @V6

安装

$ npm install react-router-dom@6

概述

  1. React Router以三个不同的包发布到npm上,它们分别为:

    1. react-router: 路由的核心库,提供了很多的:组件、钩子
    2. react-router-dom: 包含react-router所有内容,并添加一些专门用于DOM的组件,例如<BrowserRouter>
    3. react-router-native: 包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:NativeRouter
  2. 与React Router 5.x版本相比,改变了什么?

    1. 内置组件的变化:移出<Switch>,新增<Routes>
    2. 语法的变化:component={About}变为element={<About/>}
    3. 新增多个hook: useParamsuseNavigateuseMatch

示例

//index.js
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

//App.js
import React from 'react'
import { Routes, Route, Link } from 'react-router-dom'

export default function App() {
  return (
    <div>
        <h1>Welcome to React Router!</h1>
        <Routes>
            <Route path='/' element={<Home />} />
            <Route path='about' element={<About />} />
        </Routes>
    </div>
  )
}

function Home(){
    return (
        <>
            <main>
                <h2>Welcome to the homepage!</h2>
                <p>You can do this, I believe in you.</p>
            </main>
            <nav>
                <Link to="/about">About</Link>
            </nav>
        </>
    )
}

function About() {
    return (
        <>
            <main>
                <h2>Who are we?</h2>
                <p>
                    That feels like an existential question, don't you
                    think?
                </p>
            </main>
            <nav>
                <Link to="/">Home</Link>
            </nav>
        </>
    );
}

<Navigate>

  1. 作用: 只要<Navigate>组件被渲染,就会修改路径,切换视图
  2. replace属性用于控制跳转模式(push或replace,默认是push)
function Home() {
  const [sum, setSum] = useState(1)
  return (
    <div>
      个人
      {/* 根据sum的值决定是否切换视图 */}
      {sum === 2 ? <Navigate to='/about' /> : <h4>当前sum的值是:{sum}</h4> }  
      <button onClick={()=>setSum(v=>v+1)}>点我+1</button>
    </div>
  )
}

<Routes/><Route/>

  1. v6版本中移出了先前<Switch>,引入了新的替代者:<Routes/>
  2. <Routes/><Route/>要配合使用,且必须要用<Routes/>包裹<Route/>
  3. <Route/>相当于一个if语句,如果其路径与当前URL匹配,则呈现其对应的组件
  4. <Route caseSensitive/>属性用于指定:匹配时是否区分大小写(默认不区分大小写)
  5. 当URL发送变化时,<Routes/>都会查看所有子<Route/>元素以找到最佳匹配并呈现组件
  6. <Route/>也可以嵌套使用,且可配合useRoutes()配置‘路由表’,但需要通过<Outlet/>类似Vue中的<Route-view/>组件来渲染其子路由。<Outlet/>类似Vue中的<Route-view/>

<Outlet/>

  1. <Route/>产生嵌套时,渲染其对应的后续子路由,类似Vue中的<Route-view/>

在V6中,函数式组件的props已经不能接收到historylocationmatch对象,要想实现接收路由参数、编程式导航需要用useNavigateuseParamsuseSearchParamsuseLocation

useNavigate() 编程式路由导航

  1. 作用:返回一个函数用来实现编程式导航,取代history的路由跳转功能

const navigate = useNavigate()

navigate(path, options)

navigate(steps) steps>0 前进 steps<0 后退

useSearchParams() 获取search参数

  1. 作用:用于读取和修改当前位置的URL中的查询字符串
  2. 返回一个包含两个值的数组,内容分别为:当前的search参数、更新search的函数

const [search, setSearch] = useSearchParams()

const id = search.get('id')

const name = search.get('name')

useLocation() 获取state参数

  1. 作用:获取当前location信息,对标V5中路由组件的location属性

useParams() 获取params参数

useMatch()

  1. 作用:返回当前匹配信息,对标V5中路由组件的match属性