React-router使用教程

react-router 通过管理 URL,实现组件的切换和状态变化。

基本用法

React Router 的安装命令:

npm install --save react-router
// 或者
yarn add react-router

使用方式:

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>;
}

function AppRouter() {
  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 path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
  );
}

export default AppRouter;

上述示例中:

  • Router组件本身只是一个容器,真正的路由需要通过Route组件定义
  • Route组件中,path属性是指跳转的url,component属性用来定义跳转到的页面组件

注意:path只需要配置 URL 中的路径就行,不需要理会请求参数部分。

如,你的路由配置如下:

<Route path="/list" component={ListPage} />

那么以下 URL 均匹配到这个路由配置,显示ListPage

  • /list
  • /list?searchText=react
  • /list?searchText=react&beginTime=20180101&endTime=20190530

嵌套路由

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

// App组件中定义根路由
function App() {
  return (
    <Router>
      <div>
        <Header />

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

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

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

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

// 在Topics组件中定义子路由
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 App;

上述示例是在 Topics 组件中嵌套了子路由:

  • Topics组件中,传入一个match对象属性,然后我们继续在 Topics 组件定义 Link 在路径to={${match.url}/components}其中match.url返回的就是/topics,这个跳转路径实际为/topics/components
  • 在 Topics 组件中定义<Route path={${match.path}/:id} component={Topic} />,这里的 id 对应 <Link to={${match.url}/components}>Components</Link>中的components
  • 其中<Route path={${match.path}/:id} component={Topic} />就是<Route path="/topics" component={Topics} />的子路由。

Route 组件

Route是用于声明路由映射到应用程序的组件层。Route有两个特别重要的属性:pathcomponent,其中path指定路由匹配的路径(url);component定义路由对应的页面组件。

import { Route } from 'react-router-dom';

const About = () => <div>About</div>;

// 当路径匹配到`/about`时,会渲染About组件
<Route path="/about" component={About} />;

其它属性

  • exact

    完全匹配,路径必须完全一致,才能渲染对应页面

    <Route exact path="/about" component={About} /> // 路径必须为`/about`时才能渲染About,如果为`/about/:id`则不渲染
    
  • strict

    严格匹配。如果为true,则具有尾部斜杠的路径将仅匹配具有尾部斜杠的location.pathname。当location.pathname中有其他 URL 段时,这不起作用。

  • sensitive

    是否需要区分大小写,如果为 true,则在匹配路径时会区分大小写。

    <Route sensitive path="/about" component={About} /> // 路径必须为`/about`时才能渲染About,如果为`/About`则不渲染
    

Switch

Switch组件将迭代其所有子元素,并且仅渲染与当前位置匹配的第一个子元素,避免重复渲染。

<BrowserRouter>
  <div>
    <div>
      <ul>
        <li>
          <Link to="/Guide/ContactUs">ContactUs</Link>
        </li>
      </ul>
    </div>
    <Switch>
      <Route path="/Guide/ContactUs" component={ContactUs} />
      <Route path="/Guide/ContactUs" component={ContactUs} />
    </Switch>
  </div>
</BrowserRouter>

此时只会渲染一个 ContactUs 组件,而不是重复渲染两次。

Router

Router能够保持 UI 和 URL 的同步。

属性

  • children

    一个或多个Route。当history改变时,Router会匹配出Route的一个分支,并且渲染这个分支中配置的组件,渲染时保持父route组件中嵌套的子route组件。

  • history

    Router监听的history对象

页面跳转

这里我们简单介绍两种页面跳转方式:LinkpushgoBack等方法调用。

Link

允许路由跳转的主要方式。以适当的href去渲染匹配到的路由组件。可以接收到Router的状态。

<Link to="/about">About</Link> // 点击渲染`/about`匹配到的页面

上述示例,当我们点击<Link>时,URL会更新,组件会被重新渲染。其中,to指定链接到的路径。

to不仅可以是字符串,还可以 location 是对象。

除此之外,Linkreplace属性如果为 true,则单击该链接将替换历史堆栈中的当前条目,而不是添加新条目。

<Link to="/courses" replace /> //此时历史堆栈中的当前条目会被替换

调用方法

history.push()

import React from 'react';
import { withRouter } from 'react-router';

function Button() {
  const { history } = props;
  return <button onClick={() => history.push('/about')}>点击跳转</button>;
}

export default withRouter(Button);

点击Button,页面将会渲染/about路径匹配到的组件

history.goBack()

import React from 'react';
import { withRouter } from 'react-router';

function Button() {
  const { history } = props;
  return <button onClick={() => history.goBack()}>点击回退</button>;
}

export default withRouter(Button);

点击按钮,页面将会回退,渲染前一个组件。

附: 重定向(Redirect)

<Redirect>将导航到新位置。新位置将覆盖历史堆栈中的当前位置。即用户访问一个路由,会自动跳转到另一个路由。

import { Route, Redirect } from 'react-router';

<Route
  exact
  path="/"
  render={() => (loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />)}
/>;

上述示例中,如果路由匹配到/时,loggedIntrue时,路由就会重定向至/dashboard

<Switch>
  <Redirect from="/old-path" to="/new-path" />
  <Route path="/new-path" component={Place} />
</Switch>

上述示例,当路由匹配到/old-path时,会被重定向到/new-path,渲染Place组件。

数据传递

页面之间通过路由参数进行数据交互主要有三种方式:

  • 路径参数
  • 请求参数
  • history state

1. 路径参数

用于传递数据的 id。注意不要因为需要传递一些查询条件等临时数据,不要使用路径参数,而是使用下一节介绍的请求参数。


//通过路由传递gwlx和recordId参数
<Route path='/archive/dispatch/:gwlxId' component={XxxDetailPage} />


// HelloWorldPage.tsx中参数获取

import { withRouter } from 'react-router';

function HelloWorldPage(props){
    const {gwlxId,recordId} = props.match.params;
    return (<>
        <div>公文类型为:{gwlxId}</div>
        <div>数据ID为:{recordId}</div>
        </>)
}

export default withRouter(HelloWorldPage);

2. 请求参数

请求参数可以用来传递除数据 id 之外的数据。如查询条件。

首先看一下一个普通的 URL:

/archive/dispatch-list?userId=123&userName=张三

其中/archive/dispatch-list是路径部分,而?userId=123&userName=张三是查询参数,也称之为请求参数。

我们可以通过location.search获取到查询参数。可以使用qs模块解析查询参数。

const searchParams = qs.parse(
  props.location.search ? props.location.search.substr(1) : '',
);

在创建这样的 URL 时,也可以用qs模块格式化请求参数:

const searchParams = { userId: '123', userName: '张三' };
const url = `/archive/dispatch-list?${qs.stringify(searchParams)}`;
// Button.tsx

import { withRouter } from 'react-router';

function Button(props) {
  return (
    <button
      onClick={() =>
        props.history.push(`/archive/dispatch-list?userId=123&userName=张三`)
      }
    >
      点击跳转页面
    </button>
  );
}

export default withRouter(Button);

// HelloWorldPage.tsx中参数获取

import { withRouter } from 'react-router';
import qs from 'qs';

function HelloWorldPage(props) {
  const { userId, userName } = qs.parse(
    props.location.search ? props.location.search.substr(1) : '',
  );
  return (
    <>
      <div>用户名为:{userName}</div>
      <div>用户ID为:{userId}</div>
    </>
  );
}

export default withRouter(HelloWorldPage);

3. history state

使用请求参数可以传递很多数据,但是同时也会污染 URL。有时,我们只想向新页面传递一些临时的数据,又不想让用户在浏览器地址栏上看到非常长的 URL,或者不想让用户收藏这个链接,再次打开这个链接时还带着这些临时数据。如果是这样的情况,我们可以使用history state来传递数据。

请注意:如果是给列表页传递查询条件,使用请求参数。

千万注意:刷新页面或者重新打开页面,不会再有之前的history state数据。浏览器的历史回退、前进会保持页面的history state

// Button.tsx

import { withRouter } from "react-router";

function Button(props) {
  return (
    <button
      onClick={() =>
        props.history.push("url", { gwlxId: "hfw", recordId: "123" })
      }
    >
      点击跳转页面
    </button>
  );
}

export default withRouter(Button);

// HelloWorldPage.tsx中参数获取

import { withRouter } from "react-router";

function HelloWorldPage(props) {
  const { gwlxId, recordId } = props.location.state || {};
  return (
    <>
      <div>公文类型为:{gwlxId}</div>
      <div>数据ID为:{recordId}</div>
    </>
  );
}

export default withRouter(HelloWorldPage);

小结

路径参数和请求参数的方式适合传递少量的数据,history state 适合传递完整的数据对象。

总结

此教程通过描述 react-router 中的基本组件及其简单用法。更多用法请移步至react-router 官网希望我们能够快速掌握路由定义、路由配置、路由跳转、路由传参等相关技能。

参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容