react-router

原文連接https://blog.csdn.net/liangklfang/article/details/53355553javascript

上面的例子表示當頁面的路由知足了特定的格式,我就會去實例化Standarz這個router Component,那麼咱們看看實例化這個Router Component的時候會穿進去那些參數:html

 

下面我將會仔細分析一下上面的內容,不過以前我仍是想去看看Router這個Route Component自帶的history參數。java

問題2:Router這個Component的history參數有那些可選擇項react

browserHistory:表示使用瀏覽器默認的history API去檢測路由的變化,這時候服務器端必須可以處理特定的路由而後響應給客戶端;支持服務器端渲染;URI比較簡潔
hashHistory:不須要服務器端支持;不支持服務器端渲染;若是應用經過push和replace來不斷修改history,那麼咱們能夠存儲‘地址狀態’,其不會在新的地址中出現,就像咱們在HTML中經過post來提交數據同樣。hash歷史經過DOM API的window.location.hash = newHash來不斷改變,所以不存在新的地方去存儲地址狀態。可是,咱們但願全部的歷史都可以經過‘地址狀態’來存儲,所以咱們爲每個地址建立了一個獨一無二的key,並且把key存儲在sessionstorage中,當用戶點擊後退,前進的時候咱們能夠有一種機制去從新加載新的‘地址狀態’。所以,當你在一個超連接中不斷點擊的時候其後面的_k是不斷變化的,形式爲:_k=a7h1uf
createMemoryHistory:Memory history不會修改和讀取地址欄的地址,這是咱們實現服務端渲染的方式,並且他對於測試和其餘渲染環境,如React Native也是頗有用的。他和其餘兩種歷史也是不同的,由於你必須建立一個Memory History,s使用下面這種方式就能夠進行測試了:web

 

const history = createMemoryHistory(location)

若是你想要進一步個性化你的history選項,或者使用其餘方式去加強你的history,你可使用useRouterHistory。注意:useRouterHistory已是使用history的useQueries ,useBasename加強過了的。瀏覽器

 

下面是使用了basename:服務器

 

  1. import { useRouterHistory } from 'react-router'
  2.  
    import { createHistory } from 'history'
  3.  
     
  4.  
    const history = useRouterHistory(createHistory)({
  5.  
    basename: '/base-path'
  6.  
    })

下面是使用了useBeforeUnload加強:session

 

  1. import { useRouterHistory } from 'react-router'
  2.  
    import { createHistory, useBeforeUnload } from 'history' //useBeforeUnload和listenBeforeUnload
  3.  
    const history = useRouterHistory(useBeforeUnload(createHistory))()
  4.  
     
  5.  
    history.listenBeforeUnload( function () {
  6.  
    return 'Are you sure you want to leave this page?'
  7.  
    })

問題3:router還有一個routes屬性表示的是什麼react-router

 

  1.  
    let routes = <Route path="/" component={App}>
  2.  
    <Route path="/repos" component={Repos}/>
  3.  
    <Route path="/about" component={About}/>
  4.  
    </Route>;
  5.  
     
  6.  
    <Router routes={routes} history={browserHistory}/>

也就是說,這時候咱們不是Route嵌套在Router裏面,而是做爲Router的一個屬性來傳遞,這也是能夠的!若是發生了上面的路由嵌套,如加載/repos就會先加載APP,而後加載Repos,這時候咱們的APP必須經過this.props.children來實例化被嵌套組件:app

 

  1.  
    export default React.createClass({
  2.  
    render() {
  3.  
    return <div>
  4.  
    {this.props.children}
  5.  
    </div>
  6.  
    }
  7.  
    })

 

問題4:咱們先來研究上面那張圖的params屬性
咱們看看上面的路由配置:

 

<Route path="/stdize(/:name)" component={Standarz}>

咱們來詳細的看看下面的params的簽名:

 

 


也就是說,當咱們實例化Standarz的時候,能夠經過params來獲取到咱們傳入的參數,全部參數所有封裝在params對象上,以下:

 

  1. function Standarz({dispatch,content,params}) {
  2.  
    console.log( 'params',params);
  3.  
    //Object {name: "button"}
  4.  
    const contentProps={
  5.  
    dispatch,
  6.  
    content,
  7.  
    }
  8.  
    }

注意:若是遇到了通配符的時候,咱們也是能夠經過上面這種方式來取出數據:

 

  1.  
    <Router>
  2.  
    <Route path="/:userName/:id" component={UserPage}/>
  3.  
    <Route path="/about/me" component={About}/>
  4.  
    </Router>

並且這時候咱們訪問/about/me/的時候,第二個組件不會被初始化,由於第一個路由也被匹配了,因此下一個就被直接跳過了!這時候params對象上有userName和id兩個屬性!

 

通配符的規則以下:
(1):paramName
:paramName匹配URL的一個部分,直到遇到下一個/、?、#爲止。這個路徑參數能夠經過this.props.params.paramName取出。
(2)()
()表示URL的這個部分是可選的。
(3)*
*匹配任意字符,直到模式裏面的下一個字符爲止。匹配方式是非貪婪模式。
(4) **
** 匹配任意字符,直到下一個/、?、#爲止。匹配方式是貪婪模式。

問題5:上面的route是什麼?

從該圖能夠看出,其中傳入的route屬性有一個path屬性表示的是路徑,也就是咱們在Route中配置的path屬性,還有一個component屬性表示的Connect函數!

問題6:上面的routeParams屬性是什麼?

從輸出能夠看到,其和params是同樣的簽名

問題7:routes顯示的是什麼?

問題8:location指的是?

注意:這裏的key就是上面說的‘地址狀態’,若是訪問地址爲:http://localhost:8000/#/stdize/button?name=gssdy&_k=0zazfn,此時咱們的query就是對象:{name:'gssdy'},而咱們的search就是"?name=gssdy"。 location 對象,它能夠簡單的認爲是 URL 的對象形式表示,這裏要提的是 location.state,這裏 state 的含義與 HTML5 的history.pushState API 中的 state 對象同樣。每一個 URL 都會對應一個 state 對象,你能夠在對象裏存儲數據,但這個數據卻不會出如今 URL 中。實際上,數據被存在了 sessionStorage 中;

問題9:history的內部簽名是?

其內部提供了不少的方法,方法列表如上。

9.1 history.pushState({name:'Nicholas'},'Nicholas Page','1.html')

執行pushState後,新的狀態信息會被加入歷史棧中,而瀏覽器地址欄也會變成新的相對URL,可是瀏覽器不會真正向服務器發送信息,即便狀態改變以後從新查詢location.href也會返回和地址欄中相同的地址。第二個參數目前沒有瀏覽器實現,所以徹底尅傳入一個空字符串,或者一個短標題。而第一個參數應該竟可能提供頁面初始化所須要的各類信息,由於pushState會提供新的歷史狀態,你會發現後退按鈕也可以用了!按下後退按鈕會觸發window的popstate事件,其含有一個state屬性就是咱們第一個參數傳入的屬性。記住:瀏覽器加載的第一個頁面沒有狀態,所以點擊後退按鈕返回瀏覽器加載的第一個頁面時候state爲null.

9.2 history.replaceState(null,'Nicholas Page','1.html')或者忽略第二個參數

更新當前狀態,傳入的參數和pushState前兩個參數相同,調用這個方法不會再歷史棧中建立新的狀態,只會重寫當前狀態。

事實上,history/location兩個對象同時存在於路由組件的 context 中,你還能夠經過 React 的 context API 在組件的子級組件中獲取到這兩個對象。好比在 SignIn 組件的內部又包含了一個 SignInChild 組件,你就能夠在組件內部經過 this.context.history 獲取到 history 對象,進而調用它的 API 進行跳轉等操做,這部份內容能夠參考參考文獻

問題10:IndexRoute會默認加載一個組件,那麼他是如何完成的?

 

  1. <Router>
  2.  
    <Route path="/" component={App}>
  3.  
    <Route path="accounts" component={Accounts}/>
  4.  
    <Route path="statements" component={Statements}/>
  5.  
    </Route>
  6.  
    </Router>

上面代碼中,訪問根路徑/,不會加載任何子組件。也就是說,App組件的this.props.children,這時是undefined。所以,一般會採用{this.props.children||<Home/>}這樣的寫法。這時,Home明明是Accounts和Statements的同級組件,卻沒有寫在Route中。IndexRoute就是解決這個問題,顯式指定Home是根路由的子組件,即指定默認狀況下加載的子組件。你能夠把IndexRoute想象成某個路徑的index.html。

  1.  
    <Router>
  2.  
    <Route path="/" component={App}>
  3.  
    <IndexRoute component={Home}/>
  4.  
    <Route path="accounts" component={Accounts}/>
  5.  
    <Route path="statements" component={Statements}/>
  6.  
    </Route>
  7.  
    </Router>

如今,用戶訪問/的時候,加載的組件結構以下。

  1.  
    <App>
  2.  
    <Home/>
  3.  
    < /App>

這種組件結構就很清晰了:App只包含下級組件的共有元素,自己的展現內容則由Home組件定義。這樣有利於代碼分離,也有利於使用React Router提供的各類API。注意,IndexRoute組件沒有路徑參數path。

 

問題11:React-Router中還須要哪些注意點

(1)Redirect 組件,具備from和to兩個參數,to表示的是絕對路徑
(2)IndexRedirect組件表示訪問跟路由的時候實例化的組件
(3)Link組件是a標籤的React版本,具備activeStyle和activeClassName爲當前路由指定樣式
(4)連接到跟路由/應該使用IndexLink組件,而不是Link組件,由於跟路由activeStyle和activeClassName會失效,或者說老是生效,由於/會匹配任何子路由。而IndexLink組件會使用路徑的精確匹配。固然也可使用Link組件的onlyActiveOnIndex屬性
(5)若是使用表單跳轉而不是用戶點擊跳轉,可使用browserHistory.push:

  1.  
    import { browserHistory } from 'react-router'
  2.  
    // ...
  3.  
    handleSubmit(event) {
  4.  
    event.preventDefault()
  5.  
    const userName = event.target.elements[ 0].value
  6.  
    const repo = event.target.elements[ 1].value
  7.  
    const path = `/repos/${userName}/${repo}`
  8.  
    browserHistory.push(path)
  9.  
    },

  或者使用context:

  1.  
    export default React.createClass({
  2.  
    // ask for `router` from context
  3.  
    contextTypes: {
  4.  
    router: React.PropTypes.object
  5.  
    },
  6.  
    handleSubmit(event) {
  7.  
    // ...
  8.  
    this.context.router.push(path)
  9.  
    },
  10.  
    })

(6)路由的onEnter和onLeave鉤子,可使用onEnter替代Redirect組件或者作一些認證:
  

  1.  
    <Route path= "inbox" component={Inbox}>
  2.  
    <Route
  3.  
    path= "messages/:id"
  4.  
    onEnter= {
  5.  
    ({ params}, replace) => replace(`/messages/${params.id}`)
  6.  
    }
  7.  
    />
  8.  
    </Route>

 也能夠經過setRouteLeaveHook爲一個特定的Route的指定離開的函數,以下:

  1.  
    const Home = withRouter(
  2.  
    React.createClass({
  3.  
    componentDidMount() {
  4.  
    this.props.router.setRouteLeaveHook(
  5.  
    this.props.route,
  6.  
    this.routerWillLeave
  7.  
    )
  8.  
    },
  9.  
    routerWillLeave(nextLocation) {
  10.  
    // 返回 false 會繼續停留當前頁面,
  11.  
    // 不然,返回一個字符串,會顯示給用戶,讓其本身決定
  12.  
    if (! this.state.isSaved)
  13.  
    return '確認要離開?';
  14.  
    },
  15.  
    })
  16.  
    )

 

 

問題1:首先看看本身頁面中使用的一個react-router

 

  1.  
    export default function ({ history }) {
  2.  
    return (
  3.  
    <Router history={history}>
  4.  
    <Route path="/" component={HomePage} />
  5.  
    <Route path="/users" component={Users} />
  6.  
    <Route path="/stdize(/:name)" component={Standarz}>
  7.  
    </Route>
  8.  
    <Route path="*" component={NotFound} />
  9.  
    </Router>
  10.  
    );
  11.  
    }

上面的例子表示當頁面的路由知足了特定的格式,我就會去實例化Standarz這個router Component,那麼咱們看看實例化這個Router Component的時候會穿進去那些參數:

 

下面我將會仔細分析一下上面的內容,不過以前我仍是想去看看Router這個Route Component自帶的history參數。

問題2:Router這個Component的history參數有那些可選擇項

browserHistory:表示使用瀏覽器默認的history API去檢測路由的變化,這時候服務器端必須可以處理特定的路由而後響應給客戶端;支持服務器端渲染;URI比較簡潔
hashHistory:不須要服務器端支持;不支持服務器端渲染;若是應用經過push和replace來不斷修改history,那麼咱們能夠存儲‘地址狀態’,其不會在新的地址中出現,就像咱們在HTML中經過post來提交數據同樣。hash歷史經過DOM API的window.location.hash = newHash來不斷改變,所以不存在新的地方去存儲地址狀態。可是,咱們但願全部的歷史都可以經過‘地址狀態’來存儲,所以咱們爲每個地址建立了一個獨一無二的key,並且把key存儲在sessionstorage中,當用戶點擊後退,前進的時候咱們能夠有一種機制去從新加載新的‘地址狀態’。所以,當你在一個超連接中不斷點擊的時候其後面的_k是不斷變化的,形式爲:_k=a7h1uf
createMemoryHistory:Memory history不會修改和讀取地址欄的地址,這是咱們實現服務端渲染的方式,並且他對於測試和其餘渲染環境,如React Native也是頗有用的。他和其餘兩種歷史也是不同的,由於你必須建立一個Memory History,s使用下面這種方式就能夠進行測試了:

 

const history = createMemoryHistory(location)

若是你想要進一步個性化你的history選項,或者使用其餘方式去加強你的history,你可使用useRouterHistory。注意:useRouterHistory已是使用history的useQueries ,useBasename加強過了的。

 

下面是使用了basename:

 

  1.  
    import { useRouterHistory } from 'react-router'
  2.  
    import { createHistory } from 'history'
  3.  
     
  4.  
    const history = useRouterHistory(createHistory)({
  5.  
    basename: '/base-path'
  6.  
    })

下面是使用了useBeforeUnload加強:

 

 

  1.  
    import { useRouterHistory } from 'react-router'
  2.  
    import { createHistory, useBeforeUnload } from 'history' //useBeforeUnload和listenBeforeUnload
  3.  
    const history = useRouterHistory(useBeforeUnload(createHistory))()
  4.  
     
  5.  
    history.listenBeforeUnload( function () {
  6.  
    return 'Are you sure you want to leave this page?'
  7.  
    })

問題3:router還有一個routes屬性表示的是什麼

 

  1.  
    let routes = <Route path="/" component={App}>
  2.  
    <Route path="/repos" component={Repos}/>
  3.  
    <Route path="/about" component={About}/>
  4.  
    </Route>;
  5.  
     
  6.  
    <Router routes={routes} history={browserHistory}/>

也就是說,這時候咱們不是Route嵌套在Router裏面,而是做爲Router的一個屬性來傳遞,這也是能夠的!若是發生了上面的路由嵌套,如加載/repos就會先加載APP,而後加載Repos,這時候咱們的APP必須經過this.props.children來實例化被嵌套組件:

 

  1.  
    export default React.createClass({
  2.  
    render() {
  3.  
    return <div>
  4.  
    {this.props.children}
  5.  
    </div>
  6.  
    }
  7.  
    })

 

問題4:咱們先來研究上面那張圖的params屬性
咱們看看上面的路由配置:

 

<Route path="/stdize(/:name)" component={Standarz}>

咱們來詳細的看看下面的params的簽名:

 


也就是說,當咱們實例化Standarz的時候,能夠經過params來獲取到咱們傳入的參數,全部參數所有封裝在params對象上,以下:

 

  1.  
    function Standarz({dispatch,content,params}) {
  2.  
    console.log( 'params',params);
  3.  
    //Object {name: "button"}
  4.  
    const contentProps={
  5.  
    dispatch,
  6.  
    content,
  7.  
    }
  8.  
    }

注意:若是遇到了通配符的時候,咱們也是能夠經過上面這種方式來取出數據:

 

  1.  
    <Router>
  2.  
    <Route path="/:userName/:id" component={UserPage}/>
  3.  
    <Route path="/about/me" component={About}/>
  4.  
    </Router>

並且這時候咱們訪問/about/me/的時候,第二個組件不會被初始化,由於第一個路由也被匹配了,因此下一個就被直接跳過了!這時候params對象上有userName和id兩個屬性!

 

通配符的規則以下:
(1):paramName
:paramName匹配URL的一個部分,直到遇到下一個/、?、#爲止。這個路徑參數能夠經過this.props.params.paramName取出。
(2)()
()表示URL的這個部分是可選的。
(3)*
*匹配任意字符,直到模式裏面的下一個字符爲止。匹配方式是非貪婪模式。
(4) **
** 匹配任意字符,直到下一個/、?、#爲止。匹配方式是貪婪模式。

問題5:上面的route是什麼?

從該圖能夠看出,其中傳入的route屬性有一個path屬性表示的是路徑,也就是咱們在Route中配置的path屬性,還有一個component屬性表示的Connect函數!

問題6:上面的routeParams屬性是什麼?

從輸出能夠看到,其和params是同樣的簽名

問題7:routes顯示的是什麼?

問題8:location指的是?

注意:這裏的key就是上面說的‘地址狀態’,若是訪問地址爲:http://localhost:8000/#/stdize/button?name=gssdy&_k=0zazfn,此時咱們的query就是對象:{name:'gssdy'},而咱們的search就是"?name=gssdy"。 location 對象,它能夠簡單的認爲是 URL 的對象形式表示,這裏要提的是 location.state,這裏 state 的含義與 HTML5 的history.pushState API 中的 state 對象同樣。每一個 URL 都會對應一個 state 對象,你能夠在對象裏存儲數據,但這個數據卻不會出如今 URL 中。實際上,數據被存在了 sessionStorage 中;

問題9:history的內部簽名是?

其內部提供了不少的方法,方法列表如上。

9.1 history.pushState({name:'Nicholas'},'Nicholas Page','1.html')

執行pushState後,新的狀態信息會被加入歷史棧中,而瀏覽器地址欄也會變成新的相對URL,可是瀏覽器不會真正向服務器發送信息,即便狀態改變以後從新查詢location.href也會返回和地址欄中相同的地址。第二個參數目前沒有瀏覽器實現,所以徹底尅傳入一個空字符串,或者一個短標題。而第一個參數應該竟可能提供頁面初始化所須要的各類信息,由於pushState會提供新的歷史狀態,你會發現後退按鈕也可以用了!按下後退按鈕會觸發window的popstate事件,其含有一個state屬性就是咱們第一個參數傳入的屬性。記住:瀏覽器加載的第一個頁面沒有狀態,所以點擊後退按鈕返回瀏覽器加載的第一個頁面時候state爲null.

9.2 history.replaceState(null,'Nicholas Page','1.html')或者忽略第二個參數

更新當前狀態,傳入的參數和pushState前兩個參數相同,調用這個方法不會再歷史棧中建立新的狀態,只會重寫當前狀態。

事實上,history/location兩個對象同時存在於路由組件的 context 中,你還能夠經過 React 的 context API 在組件的子級組件中獲取到這兩個對象。好比在 SignIn 組件的內部又包含了一個 SignInChild 組件,你就能夠在組件內部經過 this.context.history 獲取到 history 對象,進而調用它的 API 進行跳轉等操做,這部份內容能夠參考參考文獻

問題10:IndexRoute會默認加載一個組件,那麼他是如何完成的?

 

  1.  
    <Router>
  2.  
    <Route path="/" component={App}>
  3.  
    <Route path="accounts" component={Accounts}/>
  4.  
    <Route path="statements" component={Statements}/>
  5.  
    </Route>
  6.  
    </Router>

上面代碼中,訪問根路徑/,不會加載任何子組件。也就是說,App組件的this.props.children,這時是undefined。所以,一般會採用{this.props.children||<Home/>}這樣的寫法。這時,Home明明是Accounts和Statements的同級組件,卻沒有寫在Route中。IndexRoute就是解決這個問題,顯式指定Home是根路由的子組件,即指定默認狀況下加載的子組件。你能夠把IndexRoute想象成某個路徑的index.html。

  1.  
    <Router>
  2.  
    <Route path="/" component={App}>
  3.  
    <IndexRoute component={Home}/>
  4.  
    <Route path="accounts" component={Accounts}/>
  5.  
    <Route path="statements" component={Statements}/>
  6.  
    </Route>
  7.  
    </Router>

如今,用戶訪問/的時候,加載的組件結構以下。

  1.  
    <App>
  2.  
    <Home/>
  3.  
    < /App>

這種組件結構就很清晰了:App只包含下級組件的共有元素,自己的展現內容則由Home組件定義。這樣有利於代碼分離,也有利於使用React Router提供的各類API。注意,IndexRoute組件沒有路徑參數path。

 

問題11:React-Router中還須要哪些注意點

(1)Redirect 組件,具備from和to兩個參數,to表示的是絕對路徑
(2)IndexRedirect組件表示訪問跟路由的時候實例化的組件
(3)Link組件是a標籤的React版本,具備activeStyle和activeClassName爲當前路由指定樣式
(4)連接到跟路由/應該使用IndexLink組件,而不是Link組件,由於跟路由activeStyle和activeClassName會失效,或者說老是生效,由於/會匹配任何子路由。而IndexLink組件會使用路徑的精確匹配。固然也可使用Link組件的onlyActiveOnIndex屬性
(5)若是使用表單跳轉而不是用戶點擊跳轉,可使用browserHistory.push:

  1.  
    import { browserHistory } from 'react-router'
  2.  
    // ...
  3.  
    handleSubmit(event) {
  4.  
    event.preventDefault()
  5.  
    const userName = event.target.elements[ 0].value
  6.  
    const repo = event.target.elements[ 1].value
  7.  
    const path = `/repos/${userName}/${repo}`
  8.  
    browserHistory.push(path)
  9.  
    },

  或者使用context:

  1.  
    export default React.createClass({
  2.  
    // ask for `router` from context
  3.  
    contextTypes: {
  4.  
    router: React.PropTypes.object
  5.  
    },
  6.  
    handleSubmit(event) {
  7.  
    // ...
  8.  
    this.context.router.push(path)
  9.  
    },
  10.  
    })

(6)路由的onEnter和onLeave鉤子,可使用onEnter替代Redirect組件或者作一些認證:
  

  1.  
    <Route path= "inbox" component={Inbox}>
  2.  
    <Route
  3.  
    path= "messages/:id"
  4.  
    onEnter= {
  5.  
    ({ params}, replace) => replace(`/messages/${params.id}`)
  6.  
    }
  7.  
    />
  8.  
    </Route>

 也能夠經過setRouteLeaveHook爲一個特定的Route的指定離開的函數,以下:

  1.  
    const Home = withRouter(
  2.  
    React.createClass({
  3.  
    componentDidMount() {
  4.  
    this.props.router.setRouteLeaveHook(
  5.  
    this.props.route,
  6.  
    this.routerWillLeave
  7.  
    )
  8.  
    },
  9.  
    routerWillLeave(nextLocation) {
  10.  
    // 返回 false 會繼續停留當前頁面,
  11.  
    // 不然,返回一個字符串,會顯示給用戶,讓其本身決定
  12.  
    if (! this.state.isSaved)
  13.  
    return '確認要離開?';
  14.  
    },
  15.  
    })
  16.  
    )

這部份內容能夠閱讀參考文獻「React Router使用教程」

相關文章
相關標籤/搜索