该手册是基于react-router 和 React Router 使用教程 学习编写而成,可能会有描述不够清楚的地方,大家可自行参考原文,
React Router 为 React 提供了一个完整的路由库,它允许你通过 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
,
当我们使用 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.children
为 undefined
,
当然你可以使用 {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) 都拥有 onEnter
和 onLeave
钩子,当一个路由被触发时,进入该路由时触发 onEnter
,离开该路由时触发 onLeave
,这两个钩子非常的有用,比如当进入一个路由时,需要先判断时候授权,就会可以使用 onEnter
,
在路由跳转过程中,onLeave
hook 会在所有将离开的路由中触发,从最下层的子路由开始直到最外层父路由结束,然后 onEnter
hook会从最外层的父路由开始直到最下层子路由结束,
回到概述中的例子,如果我们的路由从 /messages/5
跳转到 /about
,下面是这些 hook 的执行顺序:
/messages/:id
的onLeave
/inBox
的onLeave
/about
的onEnter
至此基础篇完结,之后我会给大家带来进阶篇,欢迎大家持续关注,