React Router DOM 中文文档(二)

history

本文档中的术语 history 指的是 history 包,它是 React Router 的两个主要依赖之一(除了 React 本身),并且提供了几种不同的实现方式,用于在各种环境中管理 JavaScript 中的会话历史。

以下术语我们会经常使用:

  • browser history - 针对 DOM 环境,用于支持 HTML5 history API 的浏览器
  • hash history - 针对 DOM 环境,用于传统的旧式(低版本) 浏览器
  • memory history - history 在内存上的实现,用于测试以及 React Native 等非 DOM 环境

history 对象通常具有以下属性和方法:

  • length - number 历史堆栈中的条目数

  • action - string 当前的导航操作(pushreplacepop

  • location - object 当前访问的位置信息,可能具有以下属性:

    • pathname - string URL 路径
    • search - string URL 中的查询字符串
    • hash - string URL 中的 hash 片段
    • state - object 存储至 location 中的额外状态数据,仅在 browser historymemory history 中有效。
  • push(path, [state]) - function 将一个新条目推入到历史堆栈中

  • replace(path, [state]) - function 替换历史堆栈中的当前条目

  • go(n) - function 将历史堆栈中的指针移动 n 个条目

  • goBack() - function 返回到上一个页面,相当于 go(-1)

  • goForward() - function 进入到下一个页面,相当于 go(1)

  • block(prompt) - function 阻止导航(请参阅 history 文档)

history is mutable

history 对象是可变的。因此建议从 <Route> 渲染组件时接收的属性中直接访问 location,而不是通过 history.location 进行访问。这样可以保证 React 在生命周期中的钩子函数正常执行。例如:

class Comp extends React.Component {
  componentWillReceiveProps(nextProps) {
    // locationChanged 会是 true
    const locationChanged = nextProps.location !== this.props.location;

    // 错误,locationChanged 永远是 false,因为 history 是可变的。
    const locationChanged = nextProps.history.location !== this.props.history.location;
  }
}

<Route component={Comp} />

根据你使用的实现方式,还可能存在其它属性。有关详细信息,请参阅 history 文档。

location

location 代表应用程序的位置。如当前的位置,将要去的位置,或是之前所在的位置。它看起来像这样:

{
  key: 'ac3df4', // 使用 hash history 时,没有这个属性
  pathname: '/somewhere'
  search: '?some=search-string',
  hash: '#howdy',
  state: {
    [userDefined]: true
  }
}

Router 将在以下几个地方为您提供一个 location 对象:

  • Route component 中,以 this.props.location 方式获取
  • Route render 中,以 ({ location }) => () 方式获取
  • Route children 中,以 ({ location }) => () 方式获取
  • withRouter 中,以 this.props.location 方式获取

location 对象永远不会发生改变,因此可以在生命周期钩子函数中使用 location 对象来查看当前访问地址是否发生改变。这种技巧在获取远程数据以及使用动画时非常有用。

componentWillReceiveProps(nextProps) {
  if (nextProps.location !== this.props.location) {
    // 已经跳转了!
  }
}

可以在以下不同情境中使用 location

通常情况下只是使用一个字符串,但是如果你需要添加一些额外的 state,以在应用程序跳转到特定位置时可以使用,那么你就可以使用 location 对象。如果你想根据导航历史而不是路径来组织 UI(如模态对话框),这也很有用(见模态画廊示例)。

// 通常情况下我们这么做
<Link to="/somewhere" />

// 但是我们可以改为使用 location 对象
const location = {
  pathname: '/somewhere',
  state: { fromDashboard: true }
};

<Link to={location} />
<Redirect to={location} />
history.push(location);
history.replace(location);

最终,location 将传递给以下组件:

这将阻止它们在 Router 状态下使用实际位置。这对动画和等待导航非常有用,或者任何时候你想诱导一个组件在不同于真实位置的地方渲染。

match

一个 match 对象包含有关 <Route path> 如何匹配 URL 的信息。它具有以下属性:

  • params - object 根据 path 中指定的动态片段,从 URL 中解析出的键值对
  • isExact - boolean 如果整个 URL 匹配(不包含尾随字符),则为 true
  • path - string 用于匹配的路径模式。可用于构建嵌套的 <Route>
  • url - string URL 的匹配部分。可用于构建嵌套的 <Link>

您可以在以下几个地方访问 match 对象:

  • Route component 中,以 this.props.match 方式获取
  • Route render 中,以 ({ match }) => () 方式获取
  • Route children 中,以 ({ match }) => () 方式获取
  • withRouter 中,以 this.props.match 方式获取
  • matchPath 的返回值

如果 <Route> 没有定义 path,并因此始终匹配,则会得到最接近的父匹配。withRouter 也是一样。

null matches

<Route path="/somewhere" children={({ match }) => ()} /> 中,即使 path 与当前位置不匹配,children 指定的内联函数也依然会被调用。这种情况下,matchnull。能够在不匹配时依然呈现 <Route> 的内容可能很有用,但是这样会带来一些挑战。

解析 URL 的默认方式是将 match.url 字符串连接到 relative-path。

`${match.url}/relative-path`

如果你在 matchnull 时尝试执行此操作,最终会出现 TypeError 错误。这意味着在使用 children 属性时尝试在 <Route> 内部连接 relative-path 是不安全的。

当您在产生空匹配对象的 <Route> 内部使用没有定义 path<Route> 时,会出现类似但更微妙的情况。

// location.pathname = '/matches'
<Route path='/does-not-match' children={({ match }) => (
  // match === null
  <Route render={({ match: pathlessMatch }) => (
    // pathlessMatch === ???
  )} />
)} />

没有 path<Route> 从它的父节点继承 match 对象。如果它的父匹配为 null,那么它的匹配也将为 null。这意味着:

  • 任何子路由/子链接必须是绝对的
  • 一个没有定义 path<Route>,它的父匹配可以为 null,但它本身需要使用 children 来呈现内容。

matchPath

在正常的渲染周期之外,你可以使用和 <Route> 所使用的相同的匹配代码,例如在服务器上呈现之前收集数据依赖关系。

import { matchPath } from 'react-router';

const match = matchPath('/users/123', {
  path: '/users/:id',
  exact: true,
  strict: false
});

pathname

第一个参数是要匹配的路径名。如果您在服务器上通过 Node.js 使用,它将是 req.path

props

第二个参数是匹配的属性,它们与 <Route> 接受的匹配属性相同:

{
  path, // 例如 /users/:id
  strict, // 可选,默认为 false
  exact // 可选,默认为false
}

withRouter

你可以通过 withRouter 高阶组件访问 history 对象的属性和最近(UI 结构上靠的最近)的 <Route>match 对象。当组件渲染时,withRouter 会将更新后的 matchlocationhistory 传递给它。

import React from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';

// 显示当前位置的 pathname 的简单组件
class ShowTheLocation extends React.Component {
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  }

  render() {
    const { match, location, history } = this.props;

    return (
      <div>You are now at {location.pathname}</div>
    );
  }
}

// 创建一个连接到 Router 的新组件(借用 redux 术语)
const ShowTheLocationWithRouter = withRouter(ShowTheLocation)

注意:withRouter 不会订阅位置更改,如 React Redux 的 connect 对状态更改所做的更改。而是在位置更改从 <Router> 组件传播出去之后重新呈现。这意味着除非其父组件重新呈现,否则使用 withRouter 不会在路由转换时重新呈现。如果使用 withRouter 来防止更新被 shouldComponentUpdate 阻塞,那么使用router 包装实现 shouldComponentUpdate 的组件是非常重要的。例如,使用 Redux 时:

// This gets around shouldComponentUpdate
withRouter(connect(...)(MyComponent))
// or
compose(
  withRouter,
  connect(...)
)(MyComponent)

// This does not
connect(...)(withRouter(MyComponent))
// nor
compose(
  connect(...),
  withRouter
)(MyComponent)

有关更多信息,请参阅本指南

静态方法和属性

封装组件的所有无反应的特定静态方法和属性都会自动复制到 connected 组件。

Component.WrappedComponent

被包装的组件被公开为返回组件上的静态属性 WrappedComponent,它可用于隔离测试组件等等。

// MyComponent.js
export default withRouter(MyComponent);

// MyComponent.test.js
import MyComponent from './MyComponent';

render(<MyComponent.WrappedComponent location={{...}} ... />);

wrappedComponentRef: func

一个将作为 ref 属性传递给包装组件的函数。

class Container extends React.Component {
  componentDidMount() {
    this.component.doSomething();
  }

  render() {
    return (
      <MyComponent wrappedComponentRef={c => this.component = c} />
    )
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容