React Router--React Router4

React的路由其實(shí)就是一個(gè)React組件,路由要通過Route組件定義。例子:

import * as React from "react";
import {IndexRedirect ,Route} from 'react-router';

import Home from '../components/home';
import ImgUpload from '../components/uploadImg';
import Login from '../components/Login';
import App from '../contains/App';

export default (
    <Route path="/" component={App}>
        <IndexRedirect to='home' />
        <Route path="home" component={Home} />
        <Route path="login" component={Login} />
        <Route path="imgLoad" component={ImgUpload} />
    </Route>
)
import * as React from 'react';
import * as ReactDom   from 'react-dom';

import { Router ,browserHistory} from 'react-router';
import routes from '../router';

ReactDom.render(
    <Router history={browserHistory} routes={routes} />,
    document.getElementById('app')
)

上面代碼中,用戶訪問根路由/home時(shí),組件會(huì)先加載App組件,然后在加載Home組件。而App組件要寫成以下:

import * as React from 'react'
import * as ReactDom  from 'react-dom'

interface IAppProps {}
interface IAppState {}

export default  class App extends React.Component<IAppProps,IAppState> {
    constructor(props) {
        super(props)
    }
    render() {
        return (
            <div>
                {this.props.children}
            </div>
        )
    }
}

上面代碼中,App組件的this.props.children屬性就是子組件。
這樣看react的路由就是一個(gè)組件,最外層的route對應(yīng)的是組建App,當(dāng)訪問/時(shí),加載App組件,而加載里面的嵌套的路由時(shí),會(huì)在App內(nèi)部的this.props.children加載相應(yīng)的組件。
IndexRoute
IndexRoute是指定當(dāng)訪問跟路由的時(shí)候this.props.children默認(rèn)加載的組件。
IndexRedirect
IndexRedirect組件用于訪問根路由的時(shí)候,將用戶重定向到某個(gè)子組件。
histroy 屬性
Router組件的history屬性,用來監(jiān)聽瀏覽器地址欄的變化,并將URL解析成一個(gè)地址對象,供 React Router 匹配。
history屬性,一共可以設(shè)置三種值
1.hashHistory:路由將通過URL的hash部分(#)切換。
2.browserHistory:瀏覽器的路由就不再通過Hash完成了,而顯示正常的路徑。
3.createMemoryHistory:主要用于服務(wù)器渲染。
路由的鉤子
每個(gè)路由都有Enter和Leave鉤子,用戶進(jìn)入或離開該路由時(shí)觸發(fā)
例如

<Route
    path="messages/:id"
    onEnter={
      ({params}, replace) => replace(`/messages/${params.id}`)
    } 
  />
React Router4

以上我們講解了Router4以前的版本,在v3中的一些思想到了v4,已經(jīng)不正確了,
1.所有的路由全部訂應(yīng)在一起。
2.布局是通過<Route>組件的嵌套而來的。
3.布局和頁面組件是完全純粹的,它們是路由的一部分。
如果以上代碼用v4實(shí)現(xiàn):

import * as React from 'react'
import { BrowserRouter, Route, Link} from 'react-router-dom'

import Home from '../components/home';
import ImgUpload from '../components/uploadImg';
import Login from '../components/Login';

export const App = () => (
    <BrowserRouter>
        <div>
             <Route path="/" exact component={Home} />
             <Route path="home" component={Home} />
             <Route path="login" component={Login} />
             <Route path="imgLoad" component={ImgUpload} />
        </div>
    </BrowserRouter>
)
import * as React from 'react';
import * as ReactDom   from 'react-dom';

import {App} from '../contains/App';

ReactDom.render(
    <App />,
    document.getElementById('app')
)

路由直接被定義到App組建里,即之前的this.props.children
產(chǎn)生了新的api:
BrowserRouter:代表瀏覽器router,而且我們現(xiàn)在從 react-router-dom 中導(dǎo)入它。
HashRouter:路由將通過URL的hash部分(#)切換。
MemoryRouter: 主要用于服務(wù)器渲染。
包容性路由
V3 的路由規(guī)則是“排他性”的,這意味著只有一條路由將獲勝,在this.props.children里面最終只加載一個(gè)組件。
但是,V4的路由規(guī)則是包容的,假如沒有exact ,那么則會(huì)加載所有組件。例如上例中,當(dāng)訪問 / 時(shí),默認(rèn)加載App組件,但是App組件下還有Home、Login、ImgUpload,這些將都被渲染。

Paste_Image.png

排他性路由
如果你只需要在路由列表里匹配一個(gè)路由,則使用 <Switch> 來啟用排他路由:

export const App = () => (
    <BrowserRouter>
        <div>
            <Switch>
                 <Route path="/" exact component={Home} />
                 <Route path="home" component={Home} />
                 <Route path="login" component={Login} />
                 <Route path="imgLoad" component={ImgUpload} />
            </Switch>
        </div>
    </BrowserRouter>
)

在此處,我們依然需要加上exact,因?yàn)闊o論你訪問 /ImgUpload
/login 主頁的路徑也將被匹配,所以會(huì)先顯示 Home,但是Switch只允許一條路由生效,所以無論你點(diǎn)login、還是ImgUpload,它永遠(yuǎn)顯示的事Home。所以在配置順序上要做一些調(diào)整,例如將 / 放到最后。
如果要是,加上 exact 則不需要考慮順序問題了。
“默認(rèn)路由”和“未找到”
V4中代替IndexRoute的 是<Route exact>;
如果沒有路由解析,則可以使用 <Switch> 與 <Redirect>重定向到具有有效路徑的默認(rèn)頁面。
嵌套布局
在V3使用嵌套布局:

export default (
    <Route path="/" component={App}>
        <IndexRedirect to='home' component={Home}/>
        <Route path="home" component={Home} />
        <Route path="login" component={Login} />
        <Route path="imgLoad" component={ImgUpload} />
        <Route path="Person" component={Person} >
            <IndexRedirect to='center'/>
            <Route path="center" component={Center} />
            <Route path="like" component={Like} />
        </Route>
    </Route>
)

app.js

export default  class App extends React.Component<IAppProps,IAppState> {
    constructor(props) {
        super(props)
    }
    render() {
        return (
            <div>
                {this.props.children}
            </div>
        )
    }
}

person.js

export default class Person extends React.Component<any,any>{
    constructor(props){
        super(props)
    }
    render(){
        return (
            <div>
                <div className="nav-wrap">
                    <ul className="nav">
                        <li><Link to="/home">首頁</Link></li>
                        <li><Link to="/imgLoad">上傳</Link></li>
                        <li><Link to="/login">登陸</Link></li>
                        <li><Link to="/Person">個(gè)人</Link></li>
                    </ul>
                </div>
                <div>
                    <ul className="nav">
                        <li><Link to="/Person/center">center</Link></li>
                        <li><Link to="/Person/like">like</Link></li>
                    </ul>
                    {this.props.children}
                </div>
            </div>
        )
    }
}

如果使用V4,我們只需要在嵌套的組件里面設(shè)計(jì):
App.js

export const App = () => (
    <BrowserRouter>
        <div>
            <Switch>
              <Route path="/" exact component={Home} />
              <Route path="/login" component={Login} />
              <Route path="/imgLoad" component={ImgUpload} />
              <Route path="/imgLoad" component={ImgUpload} />
              <Route path="/person" component={Person} />
            </Switch>
        </div>
    </BrowserRouter>
)

person.tsx

interface states  {
    id : Number,
    [propName:string] : any
}
export default class Person extends React.Component<any,states>{
    constructor(props){
        super(props)
        this.state = {
            id : 12
        }
    }
    render(){
        return (
            <div>
                <div className="nav-wrap">
                    <ul className="nav">
                        <li><Link to="/">首頁</Link></li>
                        <li><Link to="/imgLoad">上傳</Link></li>
                        <li><Link to="/login">登陸</Link></li>
                        <li><Link to="/person">person</Link></li>
                    </ul>
                </div>
                <div>
                    <ul className="nav">
                        <li><Link to="/person">center</Link></li>
                        <li><Link to="/person/like">like</Link></li>
                        <li><Link to={`/person/${this.state.id}`}>user</Link></li>
                    </ul>
                    <div>
                        this is person
                    </div>
                    <div>
                        <Switch>
                           <Route path={this.props.match.path} exact component={Center} />
                           <Route path={`${this.props.match.path}/like`} component={Like} />
                           <Route path={`${this.props.match.path}/:userId`} component={User} />
                        </Switch>
                    </div>
                </div>
            </div>
        )
    }
}

main.tsx

ReactDom.render(
    <App />,
    document.getElementById('app')
)

在組建的props中,存在一個(gè)match對象,我們打印一下:

Paste_Image.png

我們可以拿到 當(dāng)前組建的路由,因此在person.js可以這樣

<Switch>
    <Route path={this.props.match.path} exact component={Center} />
    <Route path={`${this.props.match.path}/like`} component={Like} />
    <Route path={`${this.props.match.path}/:userId`} component={User} />
</Switch>

match 對象給我們提供了幾個(gè)屬性,包括 match.params、match.path、match.url
match.path vs match.url
在上述代碼中,我們打印一下User里面的match可以發(fā)現(xiàn)
match.path:

match.path

match.url:

Paste_Image.png

一個(gè)是我們設(shè)計(jì)的路由,一個(gè)是當(dāng)前訪問的裸游;
match.path常用來構(gòu)建新的路由。
match.url常用來構(gòu)建嵌套的link

<li><Link to={`${this.props.match.url}/add`}>add</Link></li>

渲染出來:
http://localhost:3000/person/12/add
授權(quán)路由
在應(yīng)用程序中,通常會(huì)根據(jù)用戶的登錄狀態(tài)來限制用戶訪問某些路由。對于未經(jīng)授權(quán)的頁面(如“登錄”和“忘記密碼”)與已授權(quán)的頁面(應(yīng)用程序的主要部分)看起來不一樣也是常見的。為了解決這些需求,需要考慮一個(gè)應(yīng)用程序的主要入口點(diǎn):

class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <BrowserRouter>
          <Switch>
            <Route path="/auth" component={UnauthorizedLayout} />
            <AuthorizedRoute path="/app" component={PrimaryLayout} />
          </Switch>
        </BrowserRouter>
      </Provider>
    )
  }
}

使用 react-redux 與 React Router v4 非常類似,就像之前一樣,只需將 BrowserRouter
包在 <Provider>
中即可。
通過這種方法可以得到一些啟發(fā)。第一個(gè)是根據(jù)我們所在的應(yīng)用程序的哪個(gè)部分,在兩個(gè)頂層布局之間進(jìn)行選擇。像訪問 /auth/login 或 /auth/forgot-password 這樣的路徑會(huì)使用 UnauthorizedLayout —— 一個(gè)看起來適于這種情況的布局。當(dāng)用戶登錄時(shí),我們將確保所有路徑都有一個(gè) /app 前綴,它使用 AuthorizedRoute 來確定用戶是否登錄。如果用戶在沒有登錄的情況下,嘗試訪問以 /app 開頭的頁面,那么將被重定向到登錄頁面。
v4 中一個(gè)驚人的新功能是能夠?yàn)樘囟ǖ哪康膭?chuàng)建你自己的路由。它不是將 component 的屬性傳遞給 <Route>,而是傳遞一個(gè) render 回調(diào)函數(shù):

class AuthorizedRoute extends React.Component {
  componentWillMount() {
    getLoggedUser()
  }

  render() {
    const { component: Component, pending, logged, ...rest } = this.props
    return (
      <Route {...rest} render={props => {
        if (pending) return <div>Loading...</div>
        return logged
          ? <Component {...this.props} />
          : <Redirect to="/auth/login" />
      }} />
    )
  }
}

<Link> vs <NavLink>
<NavLink>與 <Link>一樣,但如果 <NavLink>匹配瀏覽器的 URL,那么它可以提供一些額外的樣式能力。例如,在示例應(yīng)用程序中,有一個(gè)<PrimaryHeader>
組件看起來像這樣:

const PrimaryHeader = () => (
  <header className="primary-header">
    <h1>Welcome to our app!</h1>
    <nav>
      <NavLink to="/app" exact activeClassName="active">Home</NavLink>
      <NavLink to="/app/users" activeClassName="active">Users</NavLink>
      <NavLink to="/app/products" activeClassName="active">Products</NavLink>
    </nav>
  </header>
)

使用 <NavLink> 可以讓我給任何一個(gè)激活的鏈接設(shè)置一個(gè) active 樣式。而且,需要注意的是,我也可以給它們添加 exact 屬性。如果沒有 exact,由于 v4 的包容性匹配策略,那么在訪問 /app/users 時(shí),主頁的鏈接將處于激活中。
NavLink 帶 exact 屬性等價(jià)于 v3 的 <link>,而且更穩(wěn)定。

參考文章:
[譯] 關(guān)于 React Router 4 的一切
All About React Router 4

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容