React Router學習

React Router教程

本教程引用馬倫老師的的教程css

React項目的可用的路由庫是React-Router,固然這也是官方支持的。它也分爲:html

  • react-router 核心組件
  • react-router-dom 應用於瀏覽器端的路由庫(單獨使用包含了react-router的核心部分)
  • react-router-native 應用於native端的路由

如下教程咱們都以Web端爲主,因此全部的教程內容都是默認關於react-router-dom的介紹。node

進行網站(將會運行在瀏覽器環境中)構建,咱們應當安裝react-router-domreact-router-dom暴露出react-router中暴露的對象與方法,所以你只須要安裝並引用react-router-dom便可。react

官方文檔地址: https://reacttraining.com/react-router/web/guides/philosophygit

Installation | 安裝

安裝:github

yarn add react-router-dom
# 或者,不使用 yarn
npm install react-router-dom

 

路由的基本概念

如今的React Router版本中已不須要路由配置,如今一切皆組件。web

ReactRouter中提供瞭如下三大組件:ajax

  • Router是全部路由組件共用的底層接口組件,它是路由規則制定的最外層的容器。
  • Route路由規則匹配,並顯示當前的規則對應的組件。
  • Link路由跳轉的組件

固然每一個組件下又會有幾種不一樣的子類組件實現。好比: Router組件就針對不一樣功能和平臺對應用:npm

  • <BrowserRouter> 瀏覽器的路由組件
  • <HashRouter> URL格式爲Hash路由組件
  • <MemoryRouter> 內存路由組件
  • <NativeRouter> Native的路由組件
  • <StaticRouter> 地址不改變的靜態路由組件

三大組件使用的關係:bootstrap

若是說咱們的應用程序是一座小城的話,那麼Route就是一座座帶有門牌號的建築物,而Link就表明了到某個建築物的路線。有了路線和目的地,那麼就缺一位老司機了,沒錯Router就是這個老司機。

第一個Demo

如今你能夠複製任意的示例代碼,並粘貼到 src/App.js。以下:

import React, { Component } from 'react';
import { HashRouter as Router, Link, Route } from 'react-router-dom';
import './App.css';

const Home = () => (
  <div>
    <h2>Home</h2>
  </div>
)

const About = () => (
  <div>
    <h2>About</h2>
  </div>
)

const Product = () => (
  <div>
    <h2>Product</h2>
  </div>
)


class App extends Component {
  render() {
    return (
      <Router>
        <div className="App">
          <Link to="/">Home</Link>
          <Link to="/About">About</Link>
          <Link to="/Product">Product</Link>
          <hr/>
          <Route path="/" exact component={Home}></Route>
          <Route path="/about" component={About}></Route>
          <Route path="/product" component={Product}></Route>
        </div>
      </Router>
    );
  }
}

export default App;

 

Router組件

BrowserRouter組件

BrowserRouter主要使用在瀏覽器中,也就是WEB應用中。它利用HTML5 的history API來同步URL和UI的變化。當咱們點擊了程序中的一個連接以後,BrowserRouter就會找出與這個URL匹配的Route,並將他們對應的組件渲染出來。 BrowserRouter是用來管理咱們的組件的,那麼它固然要被放在最頂級的位置,而咱們的應用程序的組件就做爲它的一個子組件而存在。

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
    <BrowserRouter>
        <App/>
    </BrowserRouter>,
    document.body);
 

BrowserRouter組件提供了四個屬性。

  • basename: 字符串類型,路由器的默認根路徑
  • forceRefresh: 布爾類型,在導航的過程當中整個頁面是否刷新
  • getUserConfirmation: 函數類型,當導航須要確認時執行的函數。默認是:window.confirm
  • keyLength: 數字類型location.key 的長度。默認是 6

basename 屬性

當前位置的基準 URL。若是你的頁面部署在服務器的二級(子)目錄,你須要將 basename 設置到此子目錄。正確的 URL 格式是前面有一個前導斜槓,但不能有尾部斜槓。

例如:有時候咱們的應用只是整個系統中的一個模塊,應用中的URL老是以 http://localhost/admin/ 開頭。這種狀況下咱們總不能每次定義Link和Route的時候都帶上admin吧?react-router已經考慮到了這種狀況,因此爲咱們提供了一個basename屬性。爲BrowserRouter設置了basename以後,Link中就能夠省略掉admin了,而最後渲染出來的URL又會自動帶上admin。

<BrowserRouter basename="/admin"/>
    ...
    <Link to="/home"/> // 被渲染爲 <a href="/admin/home">
    ...
</BrowserRouter>

 

getUserConfirmation: func

當導航須要確認時執行的函數。默認使用 window.confirm

// 使用默認的確認函數
const getConfirmation = (message, callback) => {
  const allowTransition = window.confirm(message)
  callback(allowTransition)
}

<BrowserRouter getUserConfirmation={getConfirmation}/>

 

forceRefresh: bool

當設置爲 true 時,在導航的過程當中整個頁面將會刷新。 只有當瀏覽器不支持 HTML5 的 history API 時,才設置爲 true

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

 

keyLength: number

location.key 的長度。默認是 6。

<BrowserRouter keyLength={12}/>

 

children: node

渲染單一子組件(元素)

HashRouter

HashRouter 使用 URL 的 hash (例如:window.location.hash) 來保持 UI 和 URL 的同步。

注意: 使用 hash 的方式記錄導航歷史不支持 location.key 和location.state。在之前的版本中,咱們爲這種行爲提供了 shim,可是仍有一些問題咱們沒法解。任何依賴此行爲的代碼或插件都將沒法正常使用。因爲該技術僅用於支持傳統的瀏覽器,所以在用於瀏覽器時可使用 <BrowserHistory> 代替。

BrowserRouter相似,它也有:basenamegetUserConfirmationchildren屬性,並且是同樣的。

hashType: string

window.location.hash 使用的 hash 類型。有以下幾種:

  • "slash" - 後面跟一個斜槓,例如 #/ 和 #/sunshine/lollipops
  • "noslash" - 後面沒有斜槓,例如 # 和 #sunshine/lollipops
  • "hashbang" - Google 風格的 "ajax crawlable",例如 #!/ 和 #!/sunshine/lollipops

默認爲 "slash"

MemoryRouter

主要用在ReactNative這種非瀏覽器的環境中,所以直接將URL的history保存在了內存中。 StaticRouter 主要用於服務端渲染。

Link組件

Link就像是一個個的路牌,爲咱們指明組件的位置。Link使用聲明式的方式爲應用程序提供導航功能,定義的Link最終會被渲染成一個a標籤。Link使用to這個屬性來指明目標組件的路徑,能夠直接使用一個字符串,也能夠傳入一個對象。

import { Link } from 'react-router-dom'
// 字符串參數
<Link to="/query">查詢</Link>

// 對象參數
<Link to={{
  pathname: '/query',
  search: '?key=name',
  hash: '#hash',
  state: { fromDashboard: true }
}}>查詢</Link>

 

屬性: to

須要跳轉到的路徑(pathname)或地址(location)。

屬性:replace: bool

當設置爲 true 時,點擊連接後將使用新地址替換掉訪問歷史記錄裏面的原地址。

當設置爲 false 時,點擊連接後將在原有訪問歷史記錄的基礎上添加一個新的紀錄。

默認爲 false

<Link to="/courses" replace />

 

NavLink是一個特殊版本的Link,可使用activeClassName來設置Link被選中時被附加的class,使用activeStyle來配置被選中時應用的樣式。此外,還有一個exact屬性,此屬性要求location徹底匹配纔會附加class和style。這裏說的匹配是指地址欄中的URl和這個Link的to指定的location相匹配。

// 選中後被添加class selected
<NavLink to={'/'} exact activeClassName='selected'>Home</NavLink>
// 選中後被附加樣式 color:red
<NavLink to={'/gallery'} activeStyle={{color:red}}>Gallery</NavLink>

 

activeClassName默認值爲 active

屬性

  • to 能夠是字符串或者對象,同Link組件
  • exact 布爾類型,徹底匹配時纔會被附件class和style
  • activeStyle Object類型
  • activeClassName 字符串類型
  • strict: bool類型,當值爲 true 時,在肯定位置是否與當前 URL 匹配時,將考慮位置 pathname 後的斜線。

Route組件

Route應該是react-route中最重要的組件了,它的做用是當location與Route的path匹配時渲染Route中的Component。若是有多個Route匹配,那麼這些Route的Component都會被渲染。

與Link相似,Route也有一個exact屬性,做用也是要求location與Route的path絕對匹配。

// 當location形如 http://location/時,Home就會被渲染。
// 由於 "/" 會匹配全部的URL,因此這裏設置一個exact來強制絕對匹配。
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>

 

Route的三種渲染方式

  1. component: 這是最經常使用也最容易理解的方式,給什麼就渲染什麼。
  2. render: render的類型是function,Route會渲染這個function的返回值。所以它的做用就是附加一些額外的邏輯。
<Route path="/home" render={() => {
    console.log('額外的邏輯');
    return (<div>Home</div>);
    }/>

 

  1. children: 這是最特殊的渲染方式。

1、它同render相似,是一個function。不一樣的地方在於它會被傳入一個match參數來告訴你這個Route的path和location匹配上沒有。
2、第二個特殊的地方在於,即便path沒有匹配上,咱們也能夠將它渲染出來。祕訣就在於前面一點提到的match參數。咱們能夠根據這個參數來決定在匹配的時候渲染什麼,不匹配的時候又渲染什麼。

// 在匹配時,容器的calss是light,<Home />會被渲染
// 在不匹配時,容器的calss是dark,<About />會被渲染
<Route path='/home' children={({ match }) => (
    <div className={match ? 'light' : 'dark'}>
      {match ? <Home/>:<About>}
    </div>
  )}/>

 

全部路由中指定的組件將被傳入如下三個 props 。

  • match.
  • location.
  • history.

這裏主要說下match.params.透過這個屬性,咱們能夠拿到從location中解析出來的參數。固然,若是想要接收參數,咱們的Route的path也要使用特殊的寫法。

以下示例,三個Link是一個文章列表中三個連接,分別指向三篇id不一樣的文章。而Route用於渲染文章詳情頁。注意path='/p/:id' ,location中的對應的段會被解析爲id=1 這樣的鍵值。最終這個鍵值會做爲param的鍵值存在。Route中的組件可使用this.props.match.params.id來獲取,示例中使用告終構賦值。

<Link to='/p/1' />
<Link to='/p/2' />
<Link to='/p/3' />
......
<Route path='/p/:id' render={(match)=<h3>當前文章ID:{match.params.id}</h3>)} />

 

location

Location 是指你當前的位置,下一步打算去的位置,或是你以前所在的位置,形式大概就像這樣:

{
  key: 'ac3df4', // 在使用 hashHistory 時,沒有 key
  pathname: '/somewhere'
  search: '?some=search-string',
  hash: '#howdy',
  state: {
    [userDefined]: true
  }
}

 

你使用如下幾種方式來獲取 location 對象:

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

你也能夠在 history.location 中獲取 location 對象,可是別那麼寫,由於 history 是可變的。更多信息請參見 history 文檔

location 對象不會發生改變,所以你能夠在生命週期的鉤子函數中使用 location 對象來查看當前頁面的位置是否發生改變,這種技巧在獲取遠程數據以及使用動畫時很是有用。

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

 

一般狀況下,你只須要給一個字符串當作 location ,可是,當你須要添加一些 location 的狀態時,你能夠對象的形式使用 location 。而且當你須要多個 UI ,而這些 UI 取決於歷史時,例如彈出框(modal),使用location 對象會有很大幫助。

// 一般你只須要這樣使用 location
<Link to="/somewhere"/>

// 可是你一樣能夠這麼用
const location = {
  pathname: '/somewhere'
  state: { fromDashboard: true }
}

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

 

最後,你能夠把 location 傳入一下組件:

  • [Route]
  • [Switch]

這樣作可讓組件不使用路由狀態(router state)中的真實 location,由於咱們有時候須要組件去渲染一個其餘的 location 而不是自己所處的真實 location,好比使用動畫或是等待跳轉時。

history

本文檔中的「history」以及「history對象」請參照 history 包中的內容。 History 是 React Router 的兩大重要依賴之一(除去 React 自己),在不一樣的 Javascript 環境中,history 以多種形式實現了對於 session 歷史的管理。

咱們會常用如下術語:

  • 「browser history」 - history 在 DOM 上的實現,常用於支持 HTML5 history API 的瀏覽器端。
  • 「hash history」 - history 在 DOM 上的實現,常用於舊版本瀏覽器端。
  • 「memory history」 - 一種存儲於內存的 history 實現,常常用於測試或是非 DOM 環境(例如 React Native)。

history 對象一般會具備如下屬性和方法:

  • length -( number 類型)指的是 history 堆棧的數量。
  • action -( string 類型)指的是當前的動做(action),例如 PUSHREPLACE 以及 POP 。
  • location -( object類型)是指當前的位置(location),location 會具備以下屬性:
    • pathname -( string 類型)URL路徑。
    • search -( string 類型)URL中的查詢字符串(query string)。
    • hash -( string 類型)URL的 hash 分段。
    • state -( string 類型)是指 location 中的狀態,例如在 push(path, state) 時,state會描述何時 location 被放置到堆棧中等信息。這個 state 只會出如今 browser history 和 memory history 的環境裏。
  • push(path, [state]) -( function 類型)在 hisotry 堆棧頂加入一個新的條目。
  • replace(path, [state]) -( function 類型)替換在 history 堆棧中的當前條目。
  • go(n) -( function 類型)將 history 對戰中的指針向前移動 n 。
  • goBack() -( function 類型)等同於 go(-1) 。
  • goForward() -( function 類型)等同於 go(1) 。
  • block(prompt) -( function 類型)阻止跳轉,(請參照 history 文檔

match

match 對象包含了 <Route path> 如何與URL匹配的信息。match 對象包含如下屬性:

  • params -( object 類型)即路徑參數,經過解析URL中動態的部分得到的鍵值對。
  • isExact - 當爲 true 時,整個URL都須要匹配。
  • path -( string 類型)用來作匹配的路徑格式。在須要嵌套 <Route> 的時候用到。
  • url -( string 類型)URL匹配的部分,在須要嵌套 <Link> 的時候會用到。

你能夠在如下地方獲取 match 對象:

  • 在 Route component 中,以 this.props.match 方式。
  • 在 Route render中,以 ({ match }) => () 方式。
  • 在 Route children中,以 ({ match }) => () 方式

Redirect組件

當這個組件被渲染是,location會被重寫爲Redirect的to指定的新location。它的一個用途是登陸重定向,好比在用戶點了登陸並驗證經過以後,將頁面跳轉到我的主頁。

<Redirect to="/new"/>

 

Switch組件

渲染匹配地址(location)的第一個 <Route>或者<Redirect>

這與只使用一堆<Route>有什麼不一樣?

<Switch>的獨特之處是獨它僅僅渲染一個路由。相反地,每個包含匹配地址(location)的<Route>都會被渲染。思考下面的代碼:

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

 

若是如今的URL是 /about ,那麼 <About><User>, 還有 <NoMatch> 都會被渲染,由於它們都與路徑(path)匹配。這種設計,容許咱們以多種方式將多個 <Route> 組合到咱們的應用程序中,例如側欄(sidebars),麪包屑(breadcrumbs),bootstrap tabs等等。 然而,偶爾咱們只想選擇一個<Route> 來渲染。若是咱們如今處於 /about,咱們也不但願匹配 /:user (或者顯示咱們的 "404" 頁面 )。如下是使用 Switch 的方法來實現:

import { Switch, Route } from 'react-router'
<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Switch>

 

 

如今,若是咱們處於 /about<Switch> 將開始尋找匹配的 <Route>。 <Route path="/about"/> 將被匹配, <Switch> 將中止尋找匹配並渲染<About>。 一樣,若是咱們處於 /michael , <User> 將被渲染。

這對於過渡動畫也是起做用的,由於匹配的 <Route> 在與前一個相同的位置被渲染。

<Fade>
  <Switch>
    {/* there will only ever be one child here */}
    {/* 這裏只會有一個子節點 */}
    <Route/>
    <Route/>
  </Switch>
</Fade>

<Fade>
  <Route/>
  <Route/>
  {/* there will always be two children here,
      one might render null though, making transitions
      a bit more cumbersome to work out */}
   {/* 這裏老是有兩個子節點,
      一個可能會渲染爲null, 使計算過渡增長了一點麻煩 */}    
</Fade>

 

Prompt組件

當用戶離開當前頁的時候作出提示. 當你的應用處在特定狀態, 此狀態不但願用戶離開時(例如填寫表格到一半), 你應該使用<Prompt>

import { Prompt } from 'react-router'

<Prompt
  when={formIsHalfFilledOut}
  message="你肯定要離開嗎?"
/>

 

message: string

當用戶嘗試導航離開時,提示用戶的消息。

<Prompt message="你肯定要離開嗎?"/>

 

message: func

會與用戶試圖前往下一個地址(location) 和 action 一塊兒被調用。

函返回一個字符串用做向用戶提示,或者返回true用做容許過渡。

<Prompt message={location => (
  `你肯定你要前往 ${location.pathname} 嗎?`
)}/>

 

when: bool

你能夠隨時渲染<Prompt>,而不是有條件地在警惕後面渲染它。

  • when={true} 時,通過用戶容許後導航
  • when={false} 時,容許不用通過用戶容許直接導航
<Prompt when={formIsHalfFilledOut} message="肯定嗎?"/>

 

路由配置

import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

// Some folks find value in a centralized route config.
// A route config is just data. React is great at mapping
// data into components, and <Route> is a component.

////////////////////////////////////////////////////////////
// first our route components
const Main = () => <h2>Main</h2>;

const Sandwiches = () => <h2>Sandwiches</h2>;

const Tacos = ({ routes }) => (
  <div>
    <h2>Tacos</h2>
    <ul>
      <li>
        <Link to="/tacos/bus">Bus</Link>
      </li>
      <li>
        <Link to="/tacos/cart">Cart</Link>
      </li>
    </ul>

    {routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
  </div>
);

const Bus = () => <h3>Bus</h3>;
const Cart = () => <h3>Cart</h3>;

////////////////////////////////////////////////////////////
// then our route config
const routes = [
  {
    path: "/sandwiches",
    component: Sandwiches
  },
  {
    path: "/tacos",
    component: Tacos,
    routes: [
      {
        path: "/tacos/bus",
        component: Bus
      },
      {
        path: "/tacos/cart",
        component: Cart
      }
    ]
  }
];

// wrap <Route> and use this everywhere instead, then when
// sub routes are added to any route it'll work
const RouteWithSubRoutes = route => (
  <Route
    path={route.path}
    render={props => (
      // pass the sub-routes down to keep nesting
      <route.component {...props} routes={route.routes} />
    )}
  />
);

const RouteConfigExample = () => (
  <Router>
    <div>
      <ul>
        <li>
          <Link to="/tacos">Tacos</Link>
        </li>
        <li>
          <Link to="/sandwiches">Sandwiches</Link>
        </li>
      </ul>

      {routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
    </div>
  </Router>
);

export default RouteConfigExample;
相關文章
相關標籤/搜索