1. 程式人生 > >初探 React Router 4.0

初探 React Router 4.0

React Router 4.0 (以下簡稱 RR4) 已經正式釋出,它遵循React的設計理念,即萬物皆元件。所以 RR4 只是一堆 提供了導航功能的元件(還有若干物件和方法),具有宣告式(引入即用),可組合性的特點。http://www.jianshu.com/p/e3adc9b5f75c

RR4 本次採用單程式碼倉庫模型架構(monorepo),這意味者這個倉庫裡面有若干相互獨立的包,分別是:

  • react-router React Router 核心
  • react-router-dom 用於 DOM 繫結的 React Router
  • react-router-native 用於 React Native 的 React Router
  • react-router-redux React Router 和 Redux 的整合
  • react-router-config 靜態路由配置的小助手

本文主要討論在 web app 中如何使用使用 RR4。

引用

react-router 還是 react-router-dom?

在 React 的使用中,我們一般要引入兩個包,react 和 react-dom,那麼 react-router 和react-router-dom 是不是兩個都要引用呢?
非也,坑就在這裡。他們兩個只要引用一個就行了,不同之處就是後者比前者多出了 <Link> <BrowserRouter>

 這樣的 DOM 類元件。
因此我們只需引用 react-router-dom 這個包就行了。當然,如果搭配 redux ,你還需要使用 react-router-redux
what is the diff between react-router-dom & react-router?

元件

<BrowserRouter>

一個使用了 HTML5 history API 的高階路由元件,保證你的 UI 介面和 URL 保持同步。此元件擁有以下屬性:

basename: string
作用:為所有位置新增一個基準URL
使用場景:假如你需要把頁面部署到伺服器的二級目錄,你可以使用 basename

 設定到此目錄。

<BrowserRouter basename="/minooo" />
<Link to="/react" /> // 最終渲染為 <a href="/minooo/react">

getUserConfirmation: func
作用:導航到此頁面前執行的函式,預設使用 window.confirm
使用場景:當需要使用者進入頁面前執行什麼操作時可用,不過一般用到的不多。

const getConfirmation = (message, callback) => {
  const allowTransition = window.confirm(message)
  callback(allowTransition)
}

<BrowserRouter getUserConfirmation={getConfirmation('Are you sure?', yourCallBack)} />

forceRefresh: bool
作用:當瀏覽器不支援 HTML5 的 history API 時強制重新整理頁面。
使用場景:同上。

const supportsHistory = 'pushState' in window.history
<BrowserRouter forceRefresh={!supportsHistory} />

keyLength: number
作用:設定它裡面路由的 location.key 的長度。預設是6。(key的作用:點選同一個連結時,每次該路由下的 location.key都會改變,可以通過 key 的變化來重新整理頁面。)
使用場景:按需設定。

<BrowserRouter keyLength={12} />

children: node
作用:渲染唯一子元素。
使用場景:作為一個 Reac t元件,天生自帶 children 屬性。

嘗試一下

<HashRouter>

Hash history 不支援 location.key 和 location.state。另外由於該技術只是用來支援舊版瀏覽器,因此更推薦大家使用 BrowserRouter,此API不再作多餘介紹。

<Route>

<Route> 也許是 RR4 中最重要的元件了,重要到你必須理解它,學會它,用好它。它最基本的職責就是當頁面的訪問地址與 Route 上的 path 匹配時,就渲染出對應的 UI 介面。

<Route> 自帶三個 render method 和三個 props 。

render methods 分別是:

  • <Route component>
  • <Route render>
  • <Route children>
    每種 render method 都有不同的應用場景,同一個<Route> 應該只使用一種 render method ,大部分情況下你將使用 component 。

props 分別是:

  • match
  • location
  • history
    所有的 render method 無一例外都將被傳入這些 props。

component
只有當訪問地址和路由匹配時,一個 React component 才會被渲染,此時此元件接受 route props (match, location, history)。
當使用 component 時,router 將使用 React.createElement 根據給定的 component 建立一個新的 React 元素。這意味著如果你使用行內函數(inline function)傳值給 component將會產生不必要的重複裝載。對於內聯渲染(inline rendering), 建議使用 renderprop。

<Route path="/user/:username" component={User} />
const User = ({ match }) => {
  return <h1>Hello {match.params.username}!</h1>
}

render: func
此方法適用於內聯渲染,而且不會產生上文說的重複裝載問題。

// 內聯渲染
<Route path="/home" render={() => <h1>Home</h1} />

// 包裝 組合
const FadingRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={props => (
    <FadeIn>
      <Component {...props} />
    </FaseIn>
  )} />
)

<FadingRoute path="/cool" component={Something} />

children: func
有時候你可能只想知道訪問地址是否被匹配,然後改變下別的東西,而不僅僅是對應的頁面。

<ul>
  <ListItemLink to="/somewhere" />
  <ListItemLink to="/somewhere-ele" />
</ul>

const ListItemLink = ({ to, ...rest }) => (
  <Route path={to} children={({ match }) => (
    <li className={match ? 'active' : ''}>
      <Link to={to} {...rest} />
    </li>
  )}
)

path: string
任何可以被 path-to-regexp解析的有效 URL 路徑

<Route path="/users/:id" component={User} />

如果不給path,那麼路由將總是匹配。

exact: bool
如果為 true,path 為 '/one' 的路由將不能匹配 '/one/two',反之,亦然。

strict: bool
對路徑末尾斜槓的匹配。如果為 true。path 為 '/one/' 將不能匹配 '/one' 但可以匹配 '/one/two'。

如果要確保路由沒有末尾斜槓,那麼 strict 和
exact 都必須同時為 true

嘗試一下

<Link>

為你的應用提供宣告式,無障礙導航。

to: string
作用:跳轉到指定路徑
使用場景:如果只是單純的跳轉就直接用字串形式的路徑。

<Link to="/courses" />

to: object
作用:攜帶引數跳轉到指定路徑
作用場景:比如你點選的這個連結將要跳轉的頁面需要展示此連結對應的內容,又比如這是個支付跳轉,需要把商品的價格等資訊傳遞過去。

<Link to={{
  pathname: '/course',
  search: '?sort=name',
  state: { price: 18 }
}} />

replace: bool
為 true 時,點選連結後將使用新地址替換掉上一次訪問的地址,什麼意思呢,比如:你依次訪問 '/one' '/two' '/three' ’/four' 這四個地址,如果回退,將依次回退至 '/three' '/two' '/one' ,這符合我們的預期,假如我們把連結 '/three' 中的 replace 設為 true 時。依次點選 one two three four 然後再回退會發生什麼呢?會依次退至 '/three' '/one'! 為此我做了個線上 demo,大家可以除錯體會一下 !

另外你能想到這個 prop 的用途是什麼呢?有人說在用 路由 做選項卡時候會用到。歡迎留言討論!

嘗試一下

<NavLink>

這是 <Link> 的特殊版,顧名思義這就是為頁面導航準備的。因為導航需要有 “啟用狀態”。

activeClassName: string
導航選中啟用時候應用的樣式名,預設樣式名為 active

<NavLink
  to="/about"
  activeClassName="selected"
>MyBlog</NavLink>

activeStyle: object
如果不想使用樣式名就直接寫style

<NavLink
  to="/about"
  activeStyle={{ color: 'green', fontWeight: 'bold' }}
>MyBlog</NavLink>

exact: bool
若為 true,只有當訪問地址嚴格匹配時啟用樣式才會應用

strict: bool
若為 true,只有當訪問地址字尾斜槓嚴格匹配(有或無)時啟用樣式才會應用

isActive: func
決定導航是否啟用,或者在導航啟用時候做點別的事情。不管怎樣,它不能決定對應頁面是否可以渲染。

嘗試一下

<Switch>

只渲染出第一個與當前訪問地址匹配的 <Route> 或 <Redirect>

思考如下程式碼,如果你訪問 /about,那麼元件 About User Nomatch 都將被渲染出來,因為他們對應的路由與訪問的地址 /about 匹配。這顯然不是我們想要的,我們只想渲染出第一個匹配的路由就可以了,於是 <Switch> 應運而生!

<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>

也許你會問,為什麼 RR4 機制裡不預設匹配第一個符合要求的呢,答:這種設計允許我們將多個 <Route> 組合到應用程式中,例如側邊欄(sidebars),麵包屑 等等。

另外,<Switch> 對於轉場動畫也非常適用,因為被渲染的路由和前一個被渲染的路由處於同一個節點位置!

<Fade>
  <Switch>
    {/* 用了Switch 這裡每次只匹配一個路由,所有隻有一個節點。 */}
    <Route/>
    <Route/>
  </Switch>
</Fade>

<Fade>
  <Route/>
  <Route/>
  {/* 不用 Switch 這裡可能就會匹配多個路由了,即便匹配不到,也會返回一個null,使動畫計算增加了一些麻煩。 */}
</Fade>

children: node
<Switch> 下的子節點只能是 <Route> 或 <Redirect> 元素。只有與當前訪問地址匹配的第一個子節點才會被渲染。<Route> 元素用它們的 path 屬性匹配,<Redirect> 元素使用它們的 from 屬性匹配。如果沒有對應的 path 或 from,那麼它們將匹配任何當前訪問地址。

嘗試一下

<Redirect>

<Redirect> 渲染時將導航到一個新地址,這個新地址覆蓋在訪問歷史資訊裡面的本該訪問的那個地址。

to: string
重定向的 URL 字串

to: object
重定向的 location 物件

push: bool
若為真,重定向操作將會把新地址加入到訪問歷史記錄裡面,並且無法回退到前面的頁面。

from: string
需要匹配的將要被重定向路徑。

嘗試一下

Prompt

當用戶離開當前頁面前做出一些提示。

message: string
當用戶離開當前頁面時,設定的提示資訊。

<Prompt message="確定要離開?" />

message: func
當用戶離開當前頁面時,設定的回掉函式

<Prompt message={location => (
  `Are you sue you want to go to ${location.pathname}?` 
)} />

when: bool
通過設定一定條件要決定是否啟用 Prompt

嘗試一下

物件和方法

history

histoty 是 RR4 的兩大重要依賴之一(另一個當然是 React 了),在不同的 javascript 環境中, history 以多種能夠行駛實現了對會話(session)歷史的管理。

我們會經常使用以下術語:

  • "browser history" - history 在 DOM 上的實現,用於支援 HTML5 history API 的瀏覽器
  • "hash history" - history 在 DOM 上的實現,用於舊版瀏覽器。
  • "memory history" - history 在記憶體上的實現,用於測試或非 DOM 環境(例如 React Native)。

history 物件通常具有以下屬性和方法:

  • length: number 瀏覽歷史堆疊中的條目數
  • action: string 路由跳轉到當前頁面執行的動作,分為 PUSH, REPLACE, POP
  • location: object 當前訪問地址資訊組成的物件,具有如下屬性:
    • pathname: string URL路徑
    • search: string URL中的查詢字串
    • hash: string URL的 hash 片段
    • state: string 例如執行 push(path, state) 操作時,location 的 state 將被提供到堆疊資訊裡,state 只有在 browser 和 memory history 有效。
  • push(path, [state]) 在歷史堆疊資訊里加入一個新條目。
  • replace(path, [state]) 在歷史堆疊資訊裡替換掉當前的條目
  • go(n) 將 history 堆疊中的指標向前移動 n。
  • goBack() 等同於 go(-1)
  • goForward 等同於 go(1)
  • block(prompt) 阻止跳轉

history 物件是可變的,因為建議從 <Route> 的 prop 裡來獲取 location,而不是從 history.location 直接獲取。這樣可以保證 React 在生命週期中的鉤子函式正常執行,例如以下程式碼:

class Comp extends React.Component {
  componentWillReceiveProps(nextProps) {
    // locationChanged
    const locationChanged = nextProps.location !== this.props.location

    // 錯誤方式,locationChanged 永遠為 false,因為history 是可變的
    const locationChanged = nextProps.history.location !== this.props.history.location
  }
}

location

location 是指你當前的位置,將要去的位置,或是之前所在的位置

{
  key: 'sdfad1'
  pathname: '/about',
  search: '?name=minooo'
  hash: '#sdfas',
  state: {
    price: 123
  }
}

在以下情境中可以獲取 location 物件

  • 在 Route component 中,以 this.props.location 獲取
  • 在 Route render 中,以 ({location}) => () 方式獲取
  • 在 Route children 中,以 ({location}) => () 方式獲取
  • 在 withRouter 中,以 this.props.location 的方式獲取

location 物件不會發生改變,因此可以在生命週期的回撥函式中使用 location 物件來檢視當前頁面的訪問地址是否發生改變。這種技巧在獲取遠端資料以及使用動畫時非常有用

componentWillReceiveProps(nextProps) {
  if (nextProps.location !== this.props.location) {
    // 已經跳轉了!
  }
}

可以在不同情境中使用 location:

  • <Link to={location} />
  • <NaviveLink to={location} />
  • <Redirect to={location />
  • history.push(location)
  • history.replace(location)

match

match 物件包含了 <Route path> 如何與 URL 匹配的資訊,具有以下屬性:

  • params: object 路徑引數,通過解析 URL 中的動態部分獲得鍵值對
  • isExact: bool 為 true 時,整個 URL 都需要匹配
  • path: string 用來匹配的路徑模式,用於建立巢狀的 <Route>
  • url: string URL 匹配的部分,用於巢狀的 <Link>

在以下情境中可以獲取 match 物件

  • 在 Route component 中,以 this.props.match獲取
  • 在 Route render 中,以 ({match}) => () 方式獲取
  • 在 Route children 中,以 ({match}) => () 方式獲取
  • 在 withRouter 中,以 this.props.match的方式獲取
  • matchPath 的返回值

當一個 Route 沒有 path 時,它會匹配一切路徑。