让前端飞

React Router v4入门学习记录: 单页应用的路由实践

2018-09-03  本文已影响2人  L2先森

React Router V4(俗称 第四代react-router)相对V2/V3几乎完全重写了,遵循 Just Component 的 API 设计理念。

本篇文章中,我们会构建一个简单的单页应用,来理解React Router的基本使用,内容:

一、如何构建单页应用?

假如你还不会构建一个简单的单页应用,请参考笔者之前的React入门文章 - react入门教程01 - 简介、开发环境搭建、创建第一个React应用

二、完整代码实现

codesandbox

三、安装

React Router V4被分成了三个包: react-routerreact-router-domreact-router-native

其中第一个react-router是不能被直接安装的。该包提供了路由的核心组件和方法。另外两个包分别提供了两个环境的路由: 浏览器原生

这里,我们构建单页网页,只需要安装react-router-dom

// npm
npm install --save react-router-dom

// yarn 
yarn add react-router-dom

四、核心概念

4.1 Router

开始具体的代码实现之前,我们需要决定使用哪一类型路由。在浏览器运行环境中,有两种路由组件,BrowserRouterHashRouter组件。在下面的单页应用中,我们使用BrowserRouter

4.2 History

每个路由对象会创建一个历史history对象,<BrowserRouter> 创建 browser history, <HashRouter> 创建 hash history。若你想深入理解history,参考这篇文章

4.3 Routes

在V3中,<Route>不是一个真正的组件,而是作为一个标签用来创建route配置对象。

使用 v4,您可以像常规的 React 程序一样布局您应用中的组件,您要根据位置(特别是其 pathname )呈现内容的任何位置,您将呈现 <Route>

在 v4,<Route> 其实是一个组件,所以无论你在哪里渲染 <Route>,它里面的内容都会被渲染。当 <Route> 的 path 与当前的路径匹配时,它将会渲染 component, render, orchildren 属性中的内容,当 <Route> 的 path 与当前的路径不匹配时,将会渲染 null

4.4 Path

Router需要一个path字符串属性,描述路由的路径地址。比如,<Route path='/roster' />会匹配以/roster为前缀的路径地址。当匹配到当前的路径时,路由会渲染一个React元素;不匹配的话,就不会渲染。

<Route path='/roster'/>
// when the pathname is '/', the path does not match
// when the pathname is '/roster' or '/roster/2', the path matches
// If you only want to match '/roster', then you need to use
// the "exact" prop. The following will match '/roster', but not
// '/roster/2'.
<Route exact path='/roster'/>
// You might find yourself adding the exact prop to most routes.
// In the future (i.e. v5), the exact prop will likely be true by
// default. For more information on that, you can check out this 
// GitHub issue:
// https://github.com/ReactTraining/react-router/issues/4958

4.5 match

当路由的path匹配时,会创建一个match对象,该对象的属性如下:

你可以通过[route tester](https://pshrmn.github.io/route-tester/#/)网站来玩转路由match实现

更多路径匹配,请看这里。�

4.6 Switch

在v3中,您可以指定一些子路由,并且只会渲染匹配到的第一个。

v4 通过 <Switch> 组件提供了相似的功能,当 <Switch> 被渲染时,它仅会渲染与当前路径匹配的第一个子 <Route>

4.6 Redirect

如果你想从一个路径重定向到另一个,在V4,你可以使用<Redirect>达到效果。

// v4
<Route exact path="/" render={() => <Redirect to="/welcome" component={App} />} />

<Switch  >
  <Route exact path="/" component={App} />
  <Route path="/login" component={Login} />
  <Redirect path="*" to="/" />
</Switch>

4.7 Link

跳转用的标签。

五、编码部分

接下来,开始实现编码部分。

5.1 <App>

首先,我们先定义一个 <App>组件。为简化实现,我们将应用分成两部分: <Header><Main>。Heander组件包含应用的导航链接;Main组件负责内容的渲染。

import React from 'react'
import Header from './Header'
import Main from './Main'

const App = () => (
  <div>
    <Header />
    <Main />
  </div>
);

export default App

5.2 创建路由

下面,我们定义该单页应用的路径地址:

    <Switch>
      <Route exact path='/' component={Home} />
      <Route exact path='/roster' component={Roster} />
      <Route exact path='/schedule' component={Schedule} />
    </Switch>

Route有三个属性可以用来渲染组件,每个<Route>只能有其中一个属性值。

<Route path='/page' component={Page} />

const extraProps = { color: 'red' }
<Route path='/page' render={(props) => (
  <Page {...props} data={extraProps}/>
)}/>

<Route path='/page' children={(props) => (
  props.match
    ? <Page {...props}/>
    : <EmptyPage {...props}/>
)}/>

通常,component属性和render属性会经常被使用,而children属性不太常用。我们暂时不需要传入额外的参数给渲染组件,所以这里的<Route>使用component属性。

上述定义了该应用的基础路由结构,接下来 给出完成的Main组件实现:

import React from 'react'
import { Switch, Route } from 'react-router-dom'
import Home from './Home'
import Roster from './Roster'
import Schedule from './Schedule'

const Main = () => (
  <main>
    <Switch>
      <Route exact path='/' component={Home} />
      <Route exact path='/roster' component={Roster} />
      <Route exact path='/schedule' component={Schedule} />
    </Switch>
  </main>
)

export default Main

5.3 嵌套路由

团队成员的个人页/roster/:number并未在上述的实现中定义。实际上,这个路由会被这个以/roster为前缀的<Roster>组件渲染。

Roster组件中,我们需要渲染两部分内容:

const Roaster = () => (
  <Switch>
    <Route exact path='/roster' component={FullRoster} />
    <Route path='/roster/:number' component={Player} />
  </Switch>
)

这种方式可以很好地将相同前缀的一组路由 统一放入一个组件中实现,更好的管理、归类。

下面是完整的<Roster>实现:

import React from 'react'
import { Switch, Route } from 'react-router-dom'
import FullRoster from './FullRoster'
import Player from './Player'

const Roster = () => (
  <div>
    <h2>This is a roster page!</h2>
    <Switch>
      <Route exact path='/roster' component={FullRoster} />
      <Route path='/roster/:number' component={Player} />
    </Switch>
  </div>
)

export default Roster

5.4 路由参数

有时候我们需要抓取路径地址中的参数,例如,团队成员的个人页需要抓取员工的工号。

/roster/:number中的:number就是需要抓取的员工工号,被储存在match.params.number。例如,/roster/6params数据是: { number: '6' } // note that the captured value is a string

<Player>组件可以使用props.match.params数据来渲染相应员工的数据。

const Player = props => {
  const player = PlayerApi.get(
    parseInt(props.match.params.number, 10)
  );
  if (!player) {
    return (<div>Sorry, but the player was not found.</div>)
  }

  return (
    <div>
      <h1>{player.name} (#{player.number})</h1>
      <h2>Position: {player.position}</h2>
      <Link to='/roster'>Back</Link>
    </div>
  );
};

5.5 <FullRoster>、<Schedule>

其他组件的实现:

// Schedule.js
import React from "react";

const Schedule = () => (
  <div>
    <ul>
      <li>6/5 @ Evergreens</li>
      <li>6/8 vs Kickers</li>
      <li>6/14 @ United</li>
    </ul>
  </div>
);

export default Schedule;

// FullRoster.js
import React from "react";
import { Link } from "react-router-dom";
import PlayerAPI from "../api";

const FullRoaster = () => (
  <div>
    <ul>
      {PlayerAPI.all().map(p => (
        <li key={p.number}>
          <Link to={`/roster/${p.number}`}>{p.name}</Link>
        </li>
      ))}
    </ul>
  </div>
);

export default FullRoaster;

六、最后

我们最后实现<Header>组件使用<Link>元素将所有页面串联起来,当你点击<Link>时,URL更新的同时,仅重新渲染相应的组件,并不需要全部重新渲染。

import React from "react";
import { Link } from "react-router-dom";

// The Header creates links that can be used to navigate
// between routes.
const Header = () => (
  <header>
    <nav>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/roster">Roster</Link>
        </li>
        <li>
          <Link to="/schedule">Schedule</Link>
        </li>
      </ul>
    </nav>
  </header>
);

export default Header;

七、参考

上一篇下一篇

猜你喜欢

热点阅读