react-router-dom的使用

参考资料

React Router中的组件

  • React Router中的组件主要分为3类,他们都从react-router-dom中获取到:
    • 路由器组件<BrowserRouter><HashRouter>
    • 路线匹配器组件<Route><Switch>
    • 导航组件<Link><NavLink><Redirect>

路由器组件

  • 每个React Router应用程序的核心应该是路由器组件
  • 对于Web项目,react-router-dom提供<BrowserRouter><HashRouter>路由器。两者之间的主要区别是它们存储URL和与Web服务器通信的方式
  • <BrowserRouter>使用常规的URL路径。这些通常是外观最好的URL,但是它们要求正确配置服务器。具体来说,您的Web服务器需要在所有由React Router客户端管理的URL上提供相同的页面。Create React App在开发中即开即用地支持此功能,并附带有关如何配置生产服务器的说明。
  • <HashRouter>当前位置存储在URL 的hash一部分中,因此URL看起来像http://example.com/#/your/page。由于哈希从不发送到服务器,因此这意味着不需要特殊的服务器配置。
  • 要使用 路由器组件,只需确保将其呈现在元素层次结构的根目录下即可。

使用方法

  1. 通常会在src-index.js中将顶级<App>元素包装在路由器组件中,如下所示:src-index.js
  2. 然后在src-APP.js中使用路线匹配器组件

路线匹配器组件

  • 路线匹配器组件:<Route><Switch>

<Route>组件

  • <Route>组件:定义路由组件。
    • path属性:(字符串)匹配路径,没有定义 path 的 <Route> 总是会被匹配。
    • exact属性:(可省略属性值)表示准确匹配路径。如不使用该属性,则默认是模糊匹配path。如果为 true,则只有在 path 完全匹配 location.pathname 时才匹配。
    • 渲染方式的属性见下方,一般选component属性。其他更多属性可参考官网

渲染方式(<Route>属性)

  • <Route>组件渲染方式(在不同的情况下使用不同的方式,只使用一种即可):
    • component属性:(组件)路径匹配成功后渲染的组件【优先使用该属性】
      • 指定只有当位置匹配时才会渲染的 React 组件,该组件会接收 route props 作为属性。具体使用方法可见下方。
    • render属性:(函数)在该路径下渲染的内容
      • 使用 render 可以方便地进行内联渲染和包装,而无需进行上文解释的不必要的组件重装。可以传入一个函数,以在位置匹配时调用,而不是使用 component 创建一个新的 React 元素。render 渲染方式接收所有与 component 方式相同的 route props。(使用方法参考官网
      • 警告<Route component> 优先于 <Route render>,因此不要在同一个 <Route>中同时使用两者。
    • children属性:(函数)在该路径下渲染的内容
      • 有时候不论 path 是否匹配位置,你都想渲染一些内容。在这种情况下,你可以使用 children 属性。除了不论是否匹配它都会被调用以外,它的工作原理与 render 完全一样。
      • 使用方法可参考官网
      • 警告<Route component> 优先于 <Route render>优先于 <Route children>,因此不要在同一个 中同时使用多个。
  • 三种渲染方式都将提供相同的三个路由属性:match、location、history

使用方法

  • <Route>组件应在<Switch>内部,他们都应该在路由器组件内部作为子元素存在。
  • 方法1:如果你已经在src-index.js中使用路由器组件包裹App组件,那么可以在src-App.js中使用<Route>组件:src-App.js
    • 注意:路由器组件中始终包含<header/>组件
    • 这意味着如果应用程序的位置是 /,那么 UI 的层次结构将会是:<header /><Home />
    • 或者,如果应用程序的位置是 /detail/1(1可以是任意id值),那么 UI 的层次结构将会是:<header /><Detail />
  • 方法2:页可以直接在src-index.js中的路由器组件内部使用<Route>组件。

<Switch>组件

  • <Switch>组件用于渲染与路径匹配的第一个<Route><Redirect>
  • <Switch>组件工作原理:
    • 当**<Switch>被渲染时,它会搜索其子元素<Route>组件,以查找路径与当前URL匹配的元素。当它找到一个时,它将渲染那个<Route>并且忽略所有其他的<Route>组件**。
    • 这意味着应该将具有更特定(通常更长的)路径的<Route>放在不太特定的路径之前
    • 如果没有<Route>匹配,<Switch>将不呈现任何内容(null)。
  • 总结:<Switch> 路由中的Switch 代表只匹配一个路由,如果不使用 <Switch> 嵌套,路由会多次匹配。

例子

在src-index.js中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import React from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Switch,
Route
} from "react-router-dom";

function App() {
return (
<div>
<Switch>
{/* 如果当前URL是/about,则渲染此路由,而其余的则被忽略 */}
<Route path="/about">
<About />
</Route>

{/* 注意这两条路由是如何排序的。更具体的路径="/contact/:id"放在路径="/contact"的前面 */}
<Route path="/contact/:id">
<Contact />
</Route>
<Route path="/contact">
<AllContacts />
</Route>

{/* 如果之前的路径都没有匹配渲染任何东西,这条路线是退路。
重要提示:带有path="/"的路由将始终能匹配到,因为所有的URL都以/开头。这是
为什么我们把这个放在最后 */}
<Route path="/">
<Home />
</Route>
</Switch>
</div>
);
}

ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
);

注意:
<Route path>匹配的是URL的开头,而不是整个URL
因此,<Route path="/">将始终与URL匹配。
因此,我们通常把这个<Route>放在<Switch>的最后。
另一种可能的解决方案是使用<Route exact path="/">,它准确匹配整个URL

导航组件

  • 导航组件:<Link><NavLink><Redirect>

<Link>组件

<Link> 组件:类似于 <a> 标签(最终也会被渲染为 a 标签)。

1
2
3
import { Link } from 'react-router-dom';

<Link to="/about">About</Link>
  • to 属性:可理解为 a 标签的 href属性。
    • 属性值可以是一个字符串形式的链接地址通过 pathname、search 和 hash 属性创建<Link to='/courses?sort=name' />
    • 也可以是对象形式的链接地址,可以具有以下任何属性:
      • pathname:要链接到的路径
      • search:查询参数
      • hash:URL 中的 hash,例如 #the-hash
      • state:存储到 location 中的额外状态数据
1
2
3
4
5
6
7
8
<Link to={{
pathname: '/courses',
search: '?sort=name',
hash: '#the-hash',
state: {
fromDashboard: true
}
}} />
  • replace属性: 布尔值,当设置为 true 时,点击链接后将替换历史堆栈中的当前条目,而不是添加新条目。默认为 false。

    1
    <Link to="/courses" replace />
  • innerRef属性: 函数,允许访问组件的底层引用。(类似普通组件的ref属性)

    1
    2
    3
    4
    5
    const refCallback = node => {
    // node 指向最终挂载的 DOM 元素,在卸载时为 null
    }

    <Link to="/" innerRef={refCallback} />

<NavLink>组件

一个特殊版本的 <Link>,它会在与当前 URL 匹配时为其呈现元素添加样式属性

1
2
3
import { NavLink } from 'react-router-dom';

<NavLink to="/about">About</NavLink>
  • activeClassName属性: string,当元素处于激活状态应用的类,默认为 active。它将与 className 属性一起使用。

    1
    <NavLink to="/faq" activeClassName="selected">FAQs</NavLink>
  • activeStyle属性: object,当元素处于激活状态应用的样式

    1
    2
    3
    4
    5
    6
    const activeStyle = {
    fontWeight: 'bold',
    color: 'red'
    };

    <NavLink to="/faq" activeStyle={activeStyle}>FAQs</NavLink>
  • exact属性: bool,默认为false,如果为 true,则只有在位置完全匹配时才应用激活类/样式

    1
    <NavLink exact to="/profile">Profile</NavLink>
  • strict属性: bool,默认为false,如果为 true,则在确定位置是否与当前 URL 匹配时,将考虑位置的路径名后面的斜杠。有关更多信息,请参阅 文档

    1
    <NavLink strict to="/events/">Events</NavLink>
  • isActive属性: func
    添加额外逻辑以确定链接是否处于激活状态的函数。如果你要做的不仅仅是验证链接的路径名与当前 URL 的路径名相匹配,那么应该使用它。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 只有当事件 id 为奇数时才考虑激活
    const oddEvent = (match, location) => {
    if (!match) {
    return false;
    }
    const eventID = parseInt(match.params.eventID);
    return !isNaN(eventID) && eventID % 2 === 1;
    }

    <NavLink to="/events/123" isActive={oddEvent}>Event 123</NavLink>
    location: object
  • isActive 属性:默认比较当前历史位置(通常是当前的浏览器 URL)。你也可以传递一个不同的位置进行比较。

<Redirect>组件 重定向

使用 <Redirect>导航到一个新的位置。新的位置将覆盖历史堆栈中的当前条目,例如服务器端重定向(HTTP 3xx)。
例子
使用<Link>类似<a>,需要点击才能跳转,而使用 <Redirect>直接导航到一个新的位置

  • to属性: string
    要重定向到的 URL,可以是 path-to-regexp 能够理解的任何有效的 URL 路径。所有要使用的 URL 参数必须由 from 提供。

    1
    <Redirect to="/somewhere/else" />
  • to属性: object
    要重定向到的位置,其中 pathname 可以是 path-to-regexp 能够理解的任何有效的 URL 路径。

    1
    2
    3
    4
    5
    6
    7
    <Redirect to={{
    pathname: '/login',
    search: '?utm=your+face',
    state: {
    referrer: currentLocation
    }
    }} />

    上例中的 state 对象可以在重定向到的组件中通过 this.props.location.state 进行访问。而 referrer 键(不是特殊名称)将通过路径名 /login 指向的登录组件中的 this.props.location.state.referrer 进行访问。

  • 更多属性可参考官方文档或者这篇笔记


路由传值

  • 在 react 中 有两种方式进行路由传值:
    • 方法1:通过动态路由传值(占位符)。类似于/a/:id/:value。在组件内部可使用 this.props.match.params.xxxx获取所传参数值。【推荐使用该方法,因为可以获取所传参数值】
      • 例子
        • src-App.js中,定义路由匹配的路径时使用动态路由传值:src-App.js符合要求就跳转Detail组件。
        • detail-index.js中,使用 this.props.match.params.id获取所传参数值,并将其传给action:detail-index.js
        • detail-store-actionCreators.js中, action根据路径中获取到的id进行不同的AJAX请求,后端接受AJAX请求后根据不同地址返回不同数据给客户端:detail-store-actionCreators.js
    • 方法2:通过 原始的 GET 路径后面,添加 ?key=value 的方式。在 组件内部 可使用 this.props.location.search的方式获取键值对(只不过获取过后还是一个字符串,需要进一步的解析才能获取所传参数值)
  • 补充:node中使用querystring获取后的参数可直接得到json格式对象

子路由的嵌套

  • 这种情况很常见,比如 A 组件内部还有许多其他的子组件。需要路由匹配选择对应的子组件时,就需要使用路由嵌套
  • 一个简单案例
  • 官方示例是在Topics下又嵌套了3个子路由:官方示例
    • 点击Topics下的Link组件时会传递对应的参数topicId(Rendering…React/Components/props-v-state),嵌套的子Route组件识别到有参数就会去渲染Topic组件,而Topic组件中的内容由传递的参数决定。
    • 关于useRouteMatch()和useParams()的用法可参考下方。注意:useRouteMatch()和useParams()都只能在 函数组件 的内部调用。
    • 案例效果可在此查看
    • path和url的区别可参考这篇文章

API 钩子

注意:以下钩子只能在 函数组件 的内部调用。

useRouteMatch()

  • useRouteMatch()尝试以与<Route>相同的方式匹配当前URL。它主要用于在不实际呈现<Route>的情况下访问匹配数据
  • 获取的match对象包含的属性见下方补充。
  • 例子:使用useRouteMatch()前后对比

补充:Route组件的match属性(对象)

  • Route组件的match属性
  • 一个match对象包含有关如何信息<Route path>相匹配的URL。match对象包含以下属性:
    • params -(对象)从与路径的动态段相对应的URL解析的键/值对
    • isExact-(布尔值)true如果整个URL都匹配(没有结尾字符)
    • path-(字符串)用于匹配的路径模式。用于构建嵌套<Route>
    • url-(字符串)URL的匹配部分。用于构建嵌套<Link>
  • path和url的区别可参考这篇文章

useParams()

  • useParams将返回URL参数的键/值对的对象。使用它来访问当前<Route>的match.params。
  • 例子:例子