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>

路由匹配和渲染规则

  1. 自顶向下
  2. 深度搜索

嵌套

<Router>
    <Route path="/" component={App}>
        <Route path="about" component={About} />
        <Route path="inbox" component={Inbox}>
            <Route path="messages/:id" component={Message} />
        </Route>
    </Route>
 </Router>
  1. 和普通的React组件一样,对于嵌套的子组件,父组件必须声明在何处容纳子组件 this.props.children
  2. 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

生命周期

history