安装命令
npm install -S react-router
基本用法
使用时,路由器Router就是React的一个组件,Router组件本身只是个容器,真正的路由要通过Route组件定义
import React from 'react';
import ReactDOM from 'react-dom';
import {Router,Route,hashHistory} from 'react-router';
import App from './App.jsx';
import About from './About.jsx';
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={App}/>
<Route path="/about" component={About}/>
</Router>
, document.getElementById('app'))
路由模式
Router组件的history属性,用来监听浏览器地址栏的变化,并将URL解析成一个地址对象,供React Router匹配,history一个可以设置三个值
- browserHistory
- hashHistory
- createMemoryHistory
如果设为browserHistory
,浏览器显示的路径是 example.com/some/path,背后调用的是浏览器的Histtory API.
注意:使用
browserHistory
时需要对服务器改造,否则用户直接向服务器请求某个子路由,会显示找不到网页404错误,如果开发服务器是webpack-dev-server 加上--history-api-fallback参数就可以了,如下在package.json中,其他服务器的配置请参考 https://github.com/ReactTraining/react-router/blob/master/docs/guides/Histories.md#configuring-your-server
"scripts": {
"dev": "webpack-dev-server --history-api-fallback --hot",
"build": "webpack --progress --hide-modules"
},
如果设为hashHistory
,路由将通过URL的hash部分#
切换,URL的形式类似 example.com/#/some/path
createMemoryHistory
主要用于服务器渲染,它创建一个内存中的history对象,不与浏览器URL互动。
const history = createMemoryHistory(location)
嵌套路由
Route组件还可以嵌套
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={Layout}>
<Route path="/index" component={App}/>
<Route path="/about" component={About}/>
</Route>
</Router>
, document.getElementById('app'))
上面代码中,用户访问/index或/about时候会先加载Layout组件,然后在Layout组件内部加载App或Abot,为此Layout组件需要加个 {this.props.children}
import React from "react"
export default React.createClass({
render(){
return (
<div>
<ul>
<li><Link to="index">Index</Link></li>
<li><Link to="about">About</Link></li>
</ul>
{this.props.children}
</div>
)
}
})
上面代码中,{this.props.children} 代表子组件 子路由也可以不写在Router组件里面,单独传入Router组件的routers属性
import React from 'react';
import ReactDOM from 'react-dom';
import {Router,Route,hashHistory,browserHistory,createMemoryHistory} from 'react-router';
import Layout from './Layout.jsx';
import App from './App.jsx';
import About from './About.jsx';
let routes = <Route component={Layout}>
<Route path="/" component={App} />
<Route path="/about" component={About} />
</Route>
ReactDOM.render(
<Router routes={routes} history={browserHistory}></Router>
, document.getElementById('app'))
path属性
Route组件的path属性指定路由的匹配规则,这个属性是可以省略,这样的话,不管路径是否匹配,总是会加载指定组件。
<Route path="home" component={Layout}>
<Route path="index" component={App} />
<Route path="about(/:name)" component={About} />
</Route>
上面的例子中当用户访问/home/about/map 的时候,会加载下面的组件
<Layout>
<About />
</Layout>
如果省略外层Route的path参数,写成下面的样子
<Route component={Layout}>
<Route path="index" component={App} />
<Route path="about/:name" component={About} />
</Route>
现在访问/about/test的时候会加载下面的组件
<Layout>
<About />
</Layout>
通配符
path属性可以使用通配符
<Route path="/hello/:name"> 匹配 /hello/michael
<Route path="/hello(/:name)> 匹配 /hello | /hello/test
<Route path="/hello/*.*> 匹配 /hello/a.jpg
<Route path="/hello/*> 匹配 /hello/ | /hello/test
<Route path="/**/*.jpg> 匹配 /hello/a.jpg | /hello/path/to/a.jpg
通配符规则如下
:paramName
匹配URL的一个部分,知道遇到下一个/、?、#
为止,这个路径参数可以通过this.props.params.paramName
取出()
表示URL的这个部分是可选的*
匹配任意字符,知道模式里面的下一个字符为止,匹配方式是非贪婪模式。**
匹配任意字符,知道下一个/、?、#
为止,匹配方式是贪婪模式。
path
属性也可以使用相对路径(不以/
开头),匹配时就会相对于父组件的路径,可以参考上一节的例子。嵌套路由如果想摆脱这个规则,可以使用绝对路由。 路由匹配规则是从上到下执行,一旦发现匹配,就不再匹配其余的规则了
<Route path="/hello" component="Hello">
<Route path="/hello" component="About">
上面的代码中,路径/hello
同时匹配两个规则,第二个规则不会生效。 设置路径参数时,需要特别小心这一点。
<Route>
<Route path="/:userName/:id" component="Hello">
<Route path="/about/me" component="About">
</Route>
上面的代码中,用户访问 /about/me
的时候永远不会触发第二个路由规则,因为匹配到第一个规则时候就匹配上了,不会向下走了,因此 带参数的路径一般要写在路由规则的底部。
此外,URL的查询字符串 /foo?bar=baz
,可以用 this.props.location.query.bar
获取
IndexRoute 组件
IndexRoute组件是为了解决默认情况下根路由的子组件问题的
<Router history={hashHistory}>
<Route path="/" component={Layout}>
<IndexRoute component={Index} />
<Route path="app" component={App} />
<Route path="about(/:name)" component={About} />
</Route>
</Router>
上面代码中,访问根路径 /
会加载Index
组件到Layout
组件的this.props.children
注意:IndexRoute组件没有路径参数path
Redirect 组件
Redirect组件用于路由的跳转,即用户访问一个路由,会自动跳转到另一个路由
<Route path="/" component={Layout}>
<Redirect from="/foo" to="/404" />
<Route path="404" component={U404} />
<Route path="app" component={App} />
<Route path="about(/:name)" component={About} />
</Route>
现在访问/foo
,就会自动跳转到/404
。
IndexRedirect 组件
IndexRedirect 组件用于访问根路由的时候,讲用户重定向到某个子路由
<Route path="/" component={Layout}>
<IndexRedirect to="/404" />
<Route path="404" component={U404} />
<Route path="app" component={App} />
<Route path="about(/:name)" component={About} />
</Route>
上面代码中,用户访问根路径时,将自动重定向到子路由中
Link
Link
组件用于取代<a>
元素,生成一个链接,允许用户点击后跳转到另一个路由。它基本上就是<a>
元素的React版本,可以接收Router的状态。
<ul>
<li><Link to="/">Index</Link></li>
<li><Link to="app">App</Link></li>
<li><Link to="about/ggg">About</Link></li>
</ul>
如果希望当前的路由与其他路由有不同样式,这时可以使用Link组件的activeStyle属性
<li><Link to="app" activeStyle={{color:'red'}}>App</Link></li>
<li><Link to="about" activeStyle={{color:'red'}}>About</Link></li>
上面代码中,当前页面的链接会红色显示。
另一种做法是,使用activeClassName
指定当前路由的Class.
<li><Link to="app" activeClassName="active">App</Link></li>
<li><Link to="about" activeClassName="active">About</Link></li>
上面代码中,当前页面的链接会红色显示。
在Router
组件之外,导航到路由页面,可以使用浏览器的History API,像下面这样写。
import { browserHistory } from 'react-router';
browserHistory.push('/some/path');
或者这样
this.context.router.push('/some/path')
IndexLink
如果链接到根路由/
,不要使用Link组件,而要使用IndexLink
组件。
这是因为对于根路由来说,activeStyle
和activeClassName
会失效,或者说总是生效,因为/
会匹配任何子路由,而IndexLink
组件会使用路径的精确匹配
<IndexLink to="/" activeStyle={{color:'red'}}>Index</IndexLink>
上面代码中,根路由只会在精确匹配时,才会变成红色
另一种方法是使用Link
组件的 onlyActiveOnIndex
属性,也能达到同样效果。
<Link to="/" activeStyle={{color:'red'}} onlyActiveOnIndex={true}>Index</Link>
实际上,IndexLink
就是对Link组件的onlyActiveOnIndex属性的包装
路由的钩子
每个路由都有Enter和Leave钩子,用户进入或离开该路由时触发。
<Route path="/app" component={App} onEnter={beforeRouter} onLeave={afterRouter} />
上面的例子中,用户进入/app
的时候会触发onEnter
钩子,用户离开/app
的时候会触发onLeave
钩子,详细例子请看下面
import React from 'react';
import ReactDOM from 'react-dom';
import {Router,Route,IndexRoute,hashHistory} from 'react-router';
import Layout from './Layout.jsx';
import App from './App.jsx';
import Index from './Index.jsx';
import About from './About.jsx';
import NotFound from './NotFound.jsx';
const beforeRouter = (nextState,replace)=>{
console.log("beforeRouter",nextState,replace)
}
const afterRouter = (nextState,replace)=>{
console.log("afterRouter",nextState,replace)
}
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={Layout}>
<IndexRoute component={Index} />
<Route path="app" component={App} onEnter={beforeRouter} onLeave={afterRouter} />
<Route path="about(/:name)" component={About} />
<Route path="*" component={NotFound} />
</Route>
</Router>
, document.getElementById('app'))