美文网首页让前端飞
React Router v4入门学习记录: 单页应用的路由实践

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

作者: L2先森 | 来源:发表于2018-09-03 22:51 被阅读2次

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

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

    • 选择哪种路由: HashRouter、BrowserRouter
    • Route的核心内容: path、exact、component、render等
    • 使用Link实现路由跳转

    一、如何构建单页应用?

    假如你还不会构建一个简单的单页应用,请参考笔者之前的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对象,该对象的属性如下:

    • url - 当前的路径
    • path - path属性值
    • isExact - path == pathname
    • params - 通过path-to-regexp从路径中抓取的数据

    你可以通过[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 创建路由

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

    • / - 首页
    • /roster - 团队执勤名单
    • /roster/:number - 团队成员的个人页
    • /schedule - 执勤安排表
        <Switch>
          <Route exact path='/' component={Home} />
          <Route exact path='/roster' component={Roster} />
          <Route exact path='/schedule' component={Schedule} />
        </Switch>
    

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

    • component - 它的值是一个组件,在path匹配成功之后会绘制这个组件;
    • render - 一个返回React组件的方法,跟component的效果差不多,但可以传入“render prop”
    • children - 返回一个React组件的方法,这个总是会绘制,即使没有匹配的路径的时候。
    <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组件中,我们需要渲染两部分内容:

    • /roster - 完整的团队执勤名单页,使用exact完全匹�配
    • /roster/:number - 团队成员的个人页,路由通过抓取/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;
    
    

    七、参考

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/kjkwwftx.html