React-Router看这里

前言

从事前端工做的第一个项目即是和同事协做构建一个大型SPA——企业后端管理平台,应用架构相似于企业微信·管理平台。使用React技术栈来完成前端开发,React-Router成为了必不可少的利器,无刷新的组件切换让用户体验更佳。
看了许多学习文档,也在工做中进行了实践,此文用于总结React-Router。html

解决的问题

使用路由能够自如流畅得向应用中添加,切换组件,并保持页面与URL间的同步,这在SPA中应用普遍。使用React-Router咱们不须要手动寻找须要渲染的组件,无需编写复杂的逻辑,只需完成相应的路由配置,其它交给路由来解决。前端

用例

import React from 'react'
import { render } from 'react-dom'

// 首先咱们须要导入一些组件...
import { Router, Route, Link } from 'react-router'

const App = React.createClass({
  render() {
    return (
      <div>
        <h1>App</h1>
        {/* 把 <a> 变成 <Link> */}
        <ul>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/inbox">Inbox</Link></li>
        </ul>

        {/*
          接着用 `this.props.children` 替换 `<Child>`
          router 会帮咱们找到这个 children
          childern所对应的组件可见于路由规则的嵌套关系中
          在此例中为About或Inbox或undefined
        */}
        {this.props.children}
      </div>
    )
  }
})

// 最后,咱们用一些 <Route> 来渲染 <Router>。
// Router是路由容器,route为真实路由,路由之间能够进行嵌套。
// 这些就是路由提供的咱们想要的东西。
React.render((
  <Router>
    <Route path="/" component={App}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
      <Route path="messages/:id" component={Message} />
    </Route>
  </Router>
), document.body)
复制代码

Router是一个React组件,做为路由容器来使用,Route中所包含的即是路由的规则。在使用时咱们可使用页面的层次关系来搭建路由规则。
在Route中有两个重要的属性:react

  • path属性置顶路由的匹配规则,将显示在浏览器的地址栏在域名后面。path属性省略时,老是会加载这个路由节点的指定组件。
  • component属性指定URL匹配此路由规则时,所要挂载的组件。

经过上面的配置,此应用会以以下方式渲染URL:算法

URL 组件
/ App
/about App -> About
/inbox App -> Inbox
/messages/[someid] App -> Message

路径语法

路由路径是匹配一个(或一部分)URL的一个字符串模式。
你是否观察到路由规则的第三条有些不太同样,它的路径是path="messages/:id",并未确切的指定一个路由地址。redux

在React-Router中有几种特殊的匹配规则:后端

  • :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/path/to/hello.jpg
复制代码

咱们可使用如用例的相对地址,也可使用以下的绝对地址,当路由规则嵌套很深时,这大有用处,能够简化咱们的路由规则。浏览器

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Index} />
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox}>
        {/* 使用 /messages/:id 替换 messages/:id */}
        <Route path="/messages/:id" component={Message} />
      </Route>
    </Route>
  </Router>
), document.body)
复制代码

经过上面的配置,此应用会以以下方式渲染URL:服务器

URL 组件
/ App -> Index
/about App -> About
/inbox App -> Inbox
/messages/[someid] App -> Inbox -> Message

提醒: 绝对路径在动态路由中没法使用。微信

History

React-Router是创建在history之上的。history知道如何去监听浏览器地址栏的变化, 并解析这个URL转化为location对象, 而后router使用它匹配到路由,最后正确地渲染对应的组件。react-router

经常使用的history有三种形式:

  • browserHistory
  • hashHistory
  • createMemoryHistory

使用方法是将history传入中:

// JavaScript 模块导入(译者注:ES6 形式)
import { browserHistory } from 'react-router'

render(
  <Router history={browserHistory} />, document.getElementById('app') ) 复制代码

React-Router推荐使用BrosbrowserHistoryerHistory,它使用浏览器中的History API来处理URL。服务器须要进行相应配合,须要服务器做好处理URL的准备。
hashHistory 使用URK中的hash(#)部分建立形如 example.com/#/some/path 的路由。 createMemoryHistory 用做SSR(服务端渲染),此处不做深刻了解。

IndexRoute 和 Redirect

IndexRoute

当咱们访问的连接为www.demo.html/时,页面将加载App组件。可是此时,App的render中的this.props.children为undefined,由于咱们的路由并不匹配App的子组件。此时,咱们可使用IndexRoute来设置一个默认页面。改造路由以下:

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Index}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
      <Route path="messages/:id" component={Message} />
    </Route>
  </Router>
), document.body)
复制代码

此时,App的render中的this.props.children将会是组件。

Redirect

思考一个问题,若是在地址栏输入了路由规则中不匹配的URL,如www.demo.html/#/noroute,会发生什么?页面会一片空白,空空如也。
咱们有这样一个需求,访问www.demo.html/#/inbox时,我想让地址跳转到www.demo.html/#/about,渲染About组件,或是修正上面的空白错误,跳转到About组件。
要解决这样的问题,咱们可使用Redirect来进行重定向。

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Index}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
      <Route path="messages/:id" component={Message} />
      {/* 跳转 /inbox 到 /about */}
      <Redirect from="inbox" to="about" />
    </Route>
  </Router>
), document.body)
复制代码

路由跳转

路由跳转能够分为内部跳转和外部跳转,内部跳转使用Link来完成,原理如a标签通常。组件外跳转可以使用push方法来完成。

Link

Link组件用于取代a标签,生成一个连接,容许用户点击后跳转到另外一个路由,如用例中的使用方法。activeStyle属性能够为当前路由添加样式。或者使用activeClassName为当前路由添加class。

<Link to="/about" activeStyle={{color: 'red'}}>About</Link>
<Link to="/inbox" activeStyle={{color: 'green'}}>Inbox</Link>
复制代码

push

须要在提交表单,点击按钮时跳转,咱们可使用push方法完成。

// somewhere like a redux/flux action file:
import { browserHistory } from 'react-router'
browserHistory.push('/some/path')
复制代码

IndexLink

若是你在这个app中使用<Link to="/">Home</Link>,它会一直处于激活状态,activeStyle和activeClassName会失效,或者说老是生效。由于全部的路由规则的开头都是 / 。这确实是个问题,由于咱们仅仅但愿在Home被渲染后,激活并连接到它。若是须要在Home路由被渲染后才激活的指向 / 的连接,即实现精确匹配,/只匹配根路由,可使用 <IndexLink to="/">Home</IndexLink>

或者使用Link组件的onlyActiveOnIndex属性,也能够达到一样的效果。

<Link to="/" activeClassName="active" onlyActiveOnIndex={true}>
  Home
</Link>
复制代码

动态路由

React-Router(Versoin 4)实现了动态路由。

对于大型应用来讲,一个首当其冲的问题就是所需加载的JavaScript的大小。程序应当只加载当前渲染页所需的JavaScript。有些开发者将这种方式称之为“代码分拆” —— 将全部的代码分拆成多个小包,在用户浏览过程当中按需加载。React-Router 里的路径匹配以及组件加载都是异步完成的,不只容许你延迟加载组件,而且能够延迟加载路由配置。Route能够定义 getChildRoutes,getIndexRoute 和 getComponents 这几个函数。它们都是异步执行,而且只有在须要时才被调用。咱们将这种方式称之为 “逐渐匹配”。 React-Router 会逐渐的匹配 URL 并只加载该URL对应页面所需的路径配置和组件。

const CourseRoute = {
  path: 'course/:courseId',

  getChildRoutes(location, callback) {
    require.ensure([], function (require) {
      callback(null, [
        require('./routes/Announcements'),
        require('./routes/Assignments'),
        require('./routes/Grades'),
      ])
    })
  },

  getIndexRoute(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Index'))
    })
  },

  getComponents(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Course'))
    })
  }
}
复制代码

Hook生命周期钩子

Route能够定义onEnter和onLeave两个hook,用于捕捉进入路由和离开路由的时间点,执行一些操做,如完成权限验证。

在路由跳转过程当中,onLeave hook 会在全部将离开的路由中触发,从最下层的子路由开始直到最外层父路由结束。而后onEnter hook会从最外层的父路由开始直到最下层子路由结束。和事件捕获与事件冒泡的机制相同。

<Router>
    <Route path="about" component={About} onEnter={/*dosomething*/} /> <Route path="inbox" component={Inbox}> <Redirect from="messages/:id" to="/messages/:id" onLeave={/*dosomething*/} /> </Route> </Router> 复制代码

上面的代码中,若是用户离开/messages/:id,进入/about时,会依次触发如下的钩子。

/messages/:id的onLeave
/inbox的onLeave
/about的onEnter
复制代码

routerWillLeave是离开当前路由时的生命周期钩子。该方法若是返回false,将阻止路由的切换,不然就返回一个字符串,在离开route前提示用户进行确认。

import { Lifecycle } from 'react-router'

const Home = React.createClass({

  // 假设 Home 是一个 route 组件,它可能会使用
  // Lifecycle mixin 去得到一个 routerWillLeave 方法。
  mixins: [ Lifecycle ],

  routerWillLeave(nextLocation) {
    if (!this.state.isSaved)
      return 'Your work is not saved! Are you sure you want to leave?'
  },

  // ...

})
复制代码

Corner

  • 路由算法会根据定义的顺序自顶向下匹配路由,匹配第一个符合规则的路由。
  • 能够经过this.props.params.id来获取 /:id 中的id。
  • URL的查询字符串/foo?bar=baz,能够用this.props.location.query.bar获取。
相关文章
相关标签/搜索