React-Router
React前端路由系统。太过简单的就不记录了。
去#去Hash
ReactRouter默认使用hashHistory, 这导致会出现不好看的URL。
形如www.tink.com/#/new?7akG
其实它match的是 /new
, 所以多出了很多不必要东西。
hashHistory是为了演示用的,生产环境中可以争取后端的支持,把路由匹配这部分的任务交给前端来做。
在 Express 下,需要这样设置:
// routes.js
module.exports = function(app){
app.get('*', function(req, res, next){
res.render('index.html')
});
}
前端,需要显式地将Router的history prop设为 browserHistory
const browserHistory = require('react-router')
// components...
<Router history={browserHistory}>
{ /* routes... */}
</Router>
路由匹配和渲染规则
- 自顶向下
- 深度搜索
嵌套
<Router>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="messages/:id" component={Message} />
</Route>
</Route>
</Router>
- 和普通的React组件一样,对于嵌套的子组件,父组件必须声明在何处容纳子组件
this.props.children
- render UI虽然可以和Url解耦, 但是一般渲染组件的顺序和其在路由中层级相一致。
/ render App
/about render App-About
/inbox render App-Inbox
/inbox/messages/1 render App-Inbox-Message
UI嵌套和路由嵌套不一致的问题
场景1. 路由不嵌套,但想要UI嵌套
messages在url规范上,可能不需要接续自inbox,但是messages的ui显然是处于inbox这个容器下的。
此时的解耦手段是:使用 绝对路由
<Router>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="/messages/:id" component={Message} />
</Route>
</Route>
</Router>
使用绝对路径,并不是从domain开始,而是相对于上一个层级的父路由而言。此处会这样匹配url
/messages/1 render App-Inbox-Message
可以看到, UI的render 和 URL的match 是解耦的 。
如果想避免用户在访问domian/inbox/messages/1
的时候,出现no match的情况,可以使用Redirect来优化一下,让访问这个url的用户,自动转向domian/messages/1
<Router>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="/messages/:id" component={Message} />
</Route>
<Redirect from="inbox/messages/:id" to="/messages/:id" />
</Route>
</Router>
场景2. UI不嵌套,但想要路由嵌套
从url的规范上来说,应该形成形如domian/stores/12/new
的url,在这个页面下进行新建
<Router>
<Route path="/" component={App}>
<Route path="stores/:storeId" component={Store}>
<Route path="items/new" component={Editing} />
<Route path="items" components={Items} />
</Route>
</Route>
</Router>
path 语法
:
作为params的占位符
<Route path="/items/:itemId" component={Item} />
这样它会匹配一个形如.../items/1
的路由时,在Item组件中,可以通过this.props.params.itemId
取到 itemId 为 1。
这种匹配会在读取到下一个 /
?
#
之后结束。
()
作为可选optional占位符
<Route path="/hello(/:name)">
可以匹配 /hello
, /hello/michael
, /hello/ryan
.
*
全部匹配
<Route path="/files/*.*">
可以匹配/files/hello.jpg
, /files/hello.html
.
被匹配到部分,会形成一个名为splat
的 param。
一般我会用来放在某一层的最后,做no match处理。
**
间隔全部匹配
<Route path="/**/*.jpg">
可以匹配/files/hello.jpg
, /files/path/to/file.jpg
,也就是读区到下一个/
?
#
之前,做全部匹配。
默认路由
路由动作
- hooks
- redirect/push
- IndexRedirect