微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

React Router 学习手册(基础篇)

该手册是基于react-routerReact Router 使用教程 学习编写而成,可能会有描述不够清楚的地方,大家可自行参考原文,

React RouterReact 提供了一个完整的路由库,它允许你通过 URl 的变化来控制组件的切换与变化

有关 React 全家桶的其余相关文章,可以查看以下链接,会持续更新

安装

使用 npm 进行安装:

npm install --save react-router

之后在需要用到的地方进行引用,

// 需要用到 ES6 编译器,比如 babel
import { Router,Route,Link } from 'react-router'

// 不需要使用编译器
var Router = require('react-router').Router
var Route = require('react-router').Route
var Link = require('react-router').Link

当然也可以使用 script 标签进行引用:

<script src="https://unpkg.com/react-router/umd/ReactRouter.min.js"></script>

之后可以通过 window.ReactRouter 进行调用

概述

样例概述

当我们想要实现类似信息系统的界面,进入到收件箱选择查看具体信息 1234 的时候,界面路由如下:

path: /inBox/messages/1234

+---------+------------+------------------------+
| About   |    InBox   |                        |
+---------+            +------------------------+
| Compose    Reply    Reply All    Archive      |
+-----------------------------------------------+
|Movie tomorrow|                                |
+--------------+   Subject: TPS Report          |
|TPS Report        From:    boss@big.co         |
+--------------+                                |
|New Pull Reque|   So ...                       |
+--------------+                                |
|...           |                                |
+--------------+--------------------------------+

应该实现以下几种路由:

URL Components
/ App -> Home
/about App -> About
/inBox App -> Indox
/inBox/messages/:id App -> InBox -> Message

使用 react-router 进行实现

// 省略部分组件定义
const App = React.createClass({
  render() {
    return (
      <div>
        <h1>App</h1>
        {/* 使用 `<Link>` 标签进行路由跳转 */}
        <ul>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/inBox">InBox</Link></li>
        </ul>
        {this.props.children}
      </div>
    )
  }
})

const About = ...;
const Home = ...;

const InBox = (props) => {
  return (
    <div>
       <h2>InBox</h2>
       {props.children}
     </div>
  )
}
const IndexStatus = ...;
const Message = ...;

// <Router> 是一个 React 组件
// <Router> 同时也是 <Route> 的一个容器,路由规则使用 <Route> 进行定义
render((
  <Router history={hasHistory}>
    <Route path="/" component={App}>
      {/*
        当我们访问 `/` 的时候不会有加载任何子组件,组件 `<App>` 的 `this.props.children`为 `undefined`,
        所以我们使用 `<IndexRoute>`  来指定认加载的子组件
      */}
      <IndexRoute compoent={Home} />
      <Route path="about" component={About} />
      <Route path="inBox" component={InBox}>
        <IndexRoute component={IndexStatus} />
        { /* 匹配 `/index/messages/123` 路由*/ }
        <Route path="messages/:id" component={Message} />
        { /* 当然我们可以直接匹配 `messages/123`,但不破坏路由组件结构 */}
        <Route component={InBox}>
          <Route path="messages/:id" component={Message} />
        </Route>
      </Route>
    </Route>
  </Router>
),document.body)

Route 详解

一个路由由三个属性来决定它是否能匹配上 URL:

  • 嵌套结构

  • Path 属性

  • 优先级

嵌套

一个 URL调用,React Router 允许你通过嵌套路由 (nested routes) 的方式来声明将要被渲染的一系列嵌套组件,嵌套路由是类树状结构 (tree-like structure),React Router 通过 route config 的顺序去匹配 URL

RouteConfig 是 React Router 内部用来指定 router 顺序的数组

<Router history={hashHistory}>
  <Route path="/" component={App}>
    <Route path="/repos" component={Repos}/>
    <Route path="/about" component={About}/>
  </Route>
</Router>

Path 语法

  • :paramName,匹配 URL一个部分,直到遇到下一个/、?、#

  • (),表示URL的这个部分是可选的

  • *,匹配任意字符(非贪婪模式),直到模式里面的下一个字符为

  • **,匹配任意字符(贪婪模式),直到下一个/、?、#为止

贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配
非贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配

<Route path="/hello/:name">         // 匹配 /hello/michael 和 /hello/ryan
<Route path="/hello(/:name)">       // 匹配 /hello,/hello/michael,和 /hello/ryan
<Route path="/files/*.*">           // 匹配 /files/hello.jpg 和 /files/hello.html
<Route path="/**/*.jpg">            // 匹配 /files/hello.jpg 和 /files/path/to/file.jpg

优先级

React Router 是通过从上到下的顺序匹配路由的,所以应该尽量保证同级路由的第一个路由不会匹配上所有可能的 Path,例如:

<Route path="/comments" ... />  
<Redirect from="/comments" ... /> // 这一条路由规则是不会执行的,以为上一条路由已经匹配了所有路径为 `/comments`

Histories

React Router 构建于 history,简而言之,React Router history 属性用于监听浏览器地址栏的变化,
并将 URL 解析后放入进 location 对象中,给 React Router 提供匹配,

我们使用如下方式从 React Router Package 中引用,

import { browserHistory } from 'react-router'

然后在 <Router> 中使用,

render(
  <Router history={browserHistory} routes={routes} />,document.getElementById('app')
)

有三种 history 属性类型:

browserHistory

browser history 是通过 URL 变化来改变路由的,它是背后调用的是浏览器的 History

但是,使用 browser history 是需要配置你的服务器

hashHistory

Hash history 使用哈希符 (#) 作为 URL 的一部分,路由通过哈希符的部分进行切换,URL 的形式类似于,example.com/#/some/path

我该使用 hashHistory 么?

Hash history 不需要你配置服务器即可使用,当你刚刚开始使用 React Router 的时候,就是用它吧,但是一般来说,生产环境下的 web 应用应该使用 browserHistory 来保持 URLs 的整洁度,并且 hashHistory不支持服务端渲染的

实际使用当中,我们会发现具体的 URL 可能为 example.com/#/some/path?_k=ckuvup

所以 ?_k=ckuvup垃圾代码什么

当我们使用 web 应用的时候,浏览器记录 (history) 通过 push 或者 replace 来产生变换,浏览器记录会存储一个地址状态 (location state),但并不会体现在 URL 中,

相关的 API 可以参考 History

而在 DOM API 中,改变 Hash history 的方式仅仅是通过 window.location.hash = newHash,这并没有办法保存地址状态,但是我们希望所有的历史记录都能够使用地址状态,所以我们为每一个地址产生一个独一无二的键值用以表示地址状态,当我们在浏览器中点击后退或者前进的时候,我们就有办法来之前的地址状态了

createMemoryHistory

Memory history 并不会从地址栏中操作或是读取,它能够帮助我们完成服务器端的渲染,或者用于测试以及其他渲染环境 (比如 React Native),和其他两种方式不一样的是,我们需要在内存中创建 history 对象来使用,

const history = createMemoryHistory(loaction)

Index Routes and Index Links

Index Routes

考虑以下代码

<Router>
  <Route path="/" component={App}>
    <Route path="accounts" component={Accounts}/>
    <Route path="statements" component={Statements}/>
  </Route>
</Router>

当我们访问 / 的时候不会有加载任何子组件,组件 <App>this.props.childrenundefined

当然你可以使用 {this.props.children || <Home />} 来定义渲染认组件,

Home 并没有出现在路由当中,所以这样写并不是非常直观,因此可以使用 <IndexRoute> 来指定当指定子组件不存在时加载认的子组件,

<Router>
  <Route path="/" component={App}>
    <IndexRoute component={Home}/>
    <Route path="accounts" component={Accounts}/>
    <Route path="statements" component={Statements}/>
  </Route>
</Router>

Redirect and Index Redirects

我们可以使用 <Redirect> 组件来定义从一个路由自动跳转至另一个路由,

<Route path="inBox" component={InBox}>
  {/* 从 /inBox/messages/:id 跳转到 /messages/:id */}
  <Redirect from="messages/:id" to="/messages/:id" />
</Route>

考虑以下代码

<Route path="/" component={App}>
  <Route path="welcome" component={Welcome} />
  <Route path="about" component={About} />
</Route>

当我们希望访问 / 的时候自动跳转welcome ,即当我们访问跟路由 / 的时候从定向为其他组件,我们可以使用 <IndexRedirect> 组件,

<Route path="/" component={App}>
  <IndexRedirect to="/welcome" />
  <Route path="welcome" component={Welcome} />
  <Route path="about" component={About} />
</Route>

Index Links

当我们想点击一个链接跳转至根路由 / , 也许我们会这么写

<Link to="/">Home</Link>

实际上它会匹配任何以 / 开始的子路由,

当我们只希望渲染 Home 相关的组件, 我们应该这么写

<IndexLink to="/">Home</IndexLink>

Enter and Leave Hooks

路由组件 (Route) 都拥有 onEnteronLeave 钩子,当一个路由被触发时,进入该路由时触发 onEnter ,离开该路由时触发 onLeave,这两个钩子非常的有用,比如当进入一个路由时,需要先判断时候授权,就会可以使用 onEnter

在路由跳转过程中,onLeave hook 会在所有将离开的路由中触发,从最下层的子路由开始直到最外层父路由结束,然后 onEnter hook会从最外层的父路由开始直到最下层子路由结束,

回到概述中的例子,如果我们的路由从 /messages/5 跳转/about,下面是这些 hook 的执行顺序:

  • /messages/:idonLeave

  • /inBoxonLeave

  • /aboutonEnter

至此基础篇完结,之后我会给大家带来进阶篇,欢迎大家持续关注,

同时如果文章中有任何错误,欢迎大家指出,好的文章需要你的支持,谢谢

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...