原文链接:https://react-guide.github.io...
react-router是一个基于react的路由库,它可以让你向应用中快速的添加视图和数据流,同时保持页面与URL之间的同步。
路由配置
1. 不使用React-Router的情形
import React from 'react'
import {render} from 'react-dom'
const About = React.createClass({...})
const Inbox = React.createClass({...})
const Home = React.createClass({...})
const App = React.createClass({
getInitialState() {
return {
route: window.location.hash.substr(1)
}
},
componentDidMount() {
window.addEventListener('hashchage', ()=> {
this.setState({
route: window.location.hash.substr(1)
})
})
},
render() {
let Child
switch (this.state.route) {
case '/about': Child = About; break;
case '/inbox': Child = Inbox; break;
default: Child = Home;
}
return (
)
}
})
React.render( , document.body)
如上,当URL的hash部分(指的是#后的部分)变化后,
我们看下在使用react-router后的编码结构:
import React from 'react'
import { render } from 'react-dom'
// 首先我们需要导入一些组件...
import { Router, Route, Link } from 'react-router'
const App = React.createClass({
render() {
return (
)
}
})
// 最后,我们用一些 来渲染 。
// 这些就是路由提供的我们想要的东西。
React.render((
), document.body)
看上面代码我们除去了对hash路由的判断,取而代之的是通过react-router控制了视图的显示。在内部,router会将你树级嵌套格式的
const routes = {
path: '/',
component: App,
childRoutes: [
{path: 'about', component: About},
{path: 'inbox', component: Inbox}
]
}
React.render()
2. 获取URL参数
当渲染组件时,React Router会自动向Route组件中注入一些有用的信息,尤其是路径中动态部分的参数。
const Message = React.createClass({
componentDidMount() {
// 来自于路径 `/inbox/messages/:id`
const id = this.props.params.id
fetchMessage(id, function (err, message) {
this.setState({ message: message })
})
},
// ...
})
3. 路由配置
路由配置是一组指令,用来告诉router如何匹配URL以及匹配后如何执行代码。
import React from 'react'
import { Router, Route, Link } from 'react-router'
const App = React.createClass({
render() {
return (
App
- About
- Inbox
{this.props.children}
)
}
})
const About = React.createClass({
render() {
return About
}
})
const Inbox = React.createClass({
render() {
return (
Inbox
{this.props.children || "Welcome to your Inbox"}
)
}
})
const Message = React.createClass({
render() {
return Message {this.props.params.id}
}
})
React.render((
), document.body)
通过上面的配置,可以看到路由是怎么渲染的:
URL | 组件 |
---|---|
/ | App |
/about | App->About |
/inbox | App->Inbox |
/inbox/messages/:id | App->Inbox->Message |
4. 添加首页
设想一下,当url为/时,我们想渲染一个在App中组件,不过此时App的render中的this.props.children还是undefined。这种情况下,可以使用IndexRoute来设置一个默认页面。
import { IndexRoute } from 'react-router'
const Dashboard = React.createClass({
render() {
return Welcome to the app!
}
})
React.render((
{/* 当 url 为/时渲染 Dashboard */}
), document.body)
此时,App的render中的this.props.children会将是
URL | 组件 |
---|---|
/ | App->Dashboard |
/about | App->About |
/inbox | App->Inbox |
/inbox/messages/:id | App->Inbox->Message |
5. 让UI从URL中解耦出来
如果我们可以将/inbox从/inbox/messages/:id中去除,并且能够让Message嵌套在App-Inbox。那我们可以通过绝对路径实现。
React.render((
{/* 使用 /messages/:id 替换 messages/:id */}
), document.body)
绝对路径可能在动态路由中无法使用
6. 兼容旧的URL
如果改为/inbox/messages/5,这样都会匹配不到路径,会返回一个错误页面,我们可以通过
import { Redirect } from 'react-router'
React.render((
{/* 跳转 /inbox/messages/:id 到 /messages/:id */}
), document.body)
现在,当有人惦记/inbox/message/5这个链接,他们会自动跳转到/message/5。
7. 替换的配置方式
因为route一般被嵌套使用,所以使用JSX这种天然具有简洁嵌套型语法的结构来描它们的关系非常方便。我们也可以使用原生route数组对象。
const routeConfig = [
{
path: '/',
component: App,
indexRoute: {
component: Dashboard
},
childRoutes: [
{ path: 'about', component: About},
{ path: 'inbox', component: Inbox, childRoutes: [
{path: '/messages/:id', component: Message },
{path: 'message/:id', onEnter: function(nextState, replaceState) {
replaceState(null, '/messages/' + nextState.params.id)
}}
]}
]
}
]
路由匹配原理
路由由三个属性来决定是否匹配一个URL:
1. 嵌套关系
2. 路径语法
3. 优先级
1. 嵌套关系
React Router使用路由嵌套的概念让你定义view的嵌套集合,当一个URL调用时,整个路由集合中(匹配的部分)都会被渲染。嵌套路由被描述成一种属性结构,Reat-Router会深度优先遍历整个路由配置来寻找一个给定的URL相匹配的路由。
2. 路径语法
路由路径是匹配一个URL的一个字符串模式,大部分的路由路径都可以直接按照字面量理解,但是以下有几个特殊的符号:
1. :paramName -- 匹配一段位于/, ?或者#之后的URL.命中的部分将被作为一个参数。
2. () -- 在它内部的内容被认为是可选的
3. * --匹配任意字符串直到命中下一个字符或者整个URL的末尾,并创建要给splat参数。
例子:
// 匹配 /hello/michael 和 /hello/ryan
// 匹配 /hello, /hello/michael 和 /hello/ryan
// 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
3. 优先级
路由算法会根据定义的顺序自顶向下匹配路由,因此,当你拥有两个兄弟路由节点匹配时,你必须确认前一个路由不会匹配后一个路由中的路径。
Histories
React Router时建立在history上的,简而言之,一个history知道如何去监听浏览器地址的变化,并解析这个URL转为location对象,然后router使用它匹配到路由,最后正确的渲染对应组件。
1. borwserHistory
2. hashHistory
3. createMemoryHistory
import {browserHistory} from 'react-router'
render(
,
document.getElementById('app')
)
1. browserHistory
Browser history 是使用 React Router 的应用推荐的 history。它使用浏览器中的 History API 用于处理 URL,创建一个像example.com/some/path这样真实的 URL 。
2. hashHistory
Hash history 使用 URL 中的 hash(#)部分去创建形如 example.com/#/some/path 的路由。
默认路由(IndexRoute)与 IndexLink
1. 默认路由
首先,我们看下不使用默认路由的情形:
当用户访问/时,App组件被渲染,但组件内的子元素却没有,App内部的this.props.children为undefined。你可以简单的使用{this.props.chidlren || ''}来渲染默认组件。
但现在,Home 无法参与到比如 onEnter hook 这些路由机制中来。 在 Home 的位置,渲染的是 Accounts 和 Statements。 由此,router 允许你使用 IndexRoute ,以使 Home 作为最高层级的路由出现.
现在 App 能够渲染 {this.props.children} 了, 我们也有了一个最高层级的路由,使 Home 可以参与进来。
2. Index Links
如果你在这个 app 中使用 Home , 它会一直处于激活状态,因为所有的 URL 的开头都是 / 。 这确实是个问题,因为我们仅仅希望在 Home 被渲染后,激活并链接到它。
如果需要在 Home 路由被渲染后才激活的指向 / 的链接,请使用
动态路由
对于大型应用来说,一个首当其冲的问题就是所需加载的 JavaScript 的大小。程序应当只加载当前渲染页所需的 JavaScript。有些开发者将这种方式称之为“代码分拆” —— 将所有的代码分拆成多个小包,在用户浏览过程中按需加载。
路由是个非常适于做代码分拆的地方:它的责任就是配置好每个 view。
React Router 里的路径匹配以及组件加载都是异步完成的,不仅允许你延迟加载组件,并且可以延迟加载路由配置。在首次加载包中你只需要有一个路径定义,路由会自动解析剩下的路径。
Route 可以定义 getChildRoutes,getIndexRoute 和 getComponents 这几个函数。它们都是异步执行,并且只有在需要时才被调用。我们将这种方式称之为 “逐渐匹配”。 React Router 会逐渐的匹配 URL 并只加载该 URL 对应页面所需的路径配置和组件。