React Router v4 版本 完全指北

發表於2017-10-31

React Router 事實上是React官方的標準路由庫。當你在一個多檢視的React應用中來回切換,你需要一個路由來管理那些URL。React Router 專注於此,同步保持你應用的UI和URL。

這個教程主要給你介紹React Router 的v4版本,以及你使用它可以做的大部分事情。

開場白

React 是一個很流行的庫,用於在客戶端渲染建立的單頁應用(SPAs)。 一個SPA會有很多檢視(也可以稱為頁面),不像傳統的多頁應用,檢視之間的跳轉不應該導致整個頁面被重新載入。相反,我們希望檢視就在當前頁面裡渲染。那些習慣於多頁應用的終端使用者,期望在一個SPA中應該包含以下特性:

  • 應用中每個檢視都應該有對應的唯一URL用來區分檢視。以便使用者可以在之後通過書籤收藏的URL指向引用資源 – 例如:www.example.com/products
  • 瀏覽器的前進後退按鈕應該正常工作。
  • 動態生成的巢狀檢視更應該有成對應的URL – 例如:example.com/products/shoes/101,101是產品id。

路由跳轉是指在同步保持瀏覽器URL的過程中渲染頁面中的檢視。React Router 讓你宣告式的操作路由跳轉。宣告式路由方法,通過說“路由應該是這樣的”,允許你控制應用中的資料流:

你可以把<Router>元件放在任意你想要路由渲染的地方。由於我們所需要接觸的<Router>,<Link>以及其他React Router的API都只是元件,所以你可以非常方便的在React裡使用路由。

寫在開頭。有一個常見的誤區,大家都認為React Router是由facebook官方開發的一個路由解決方案。實際上,它是一個因其設計和簡易性而流行的第三方庫。如果你的需求只侷限於路由的跳轉,你可以無需太多麻煩,就可以從頭開始實現一個自定義的路由。但是,瞭解React Router的基礎知識可以讓你更清楚的認識一個路由是怎麼工作的。

概述

React Router Logo

本次教程分為幾個部分。首先,我們使用npm安裝好React和React Router,然後我們就開始React Router的基礎部分。你將會看到React Router不同的程式碼示例的效果。本次教程涉及的例子包含:

  1. 基本路由跳轉
  2. 巢狀路由
  3. 帶路徑引數的巢狀路由
  4. 保護式路由

主要圍繞構建這些路由所涉及的概念進行討論。這個專案的全部程式碼在這個Github倉庫可以看到。當你進入一個單獨的demo目錄,執行npm install來安裝依賴。要在本地伺服器上執行這個應用,執行npm start,然後在瀏覽器開啟http://localhost:3000/可以看到執行的demo。

讓我們開始吧!

安裝 React Router

假設你已經有一個React開發環境並已經執行了。如果沒有,可以跳轉到React和JSX入門。或者,你可以使用Create React App來生成建立一個基本的React專案所需要的檔案。這是Create React App生成的預設目錄結構:

React Router庫包含三個包: react-router, react-router-dom, 和 react-router-nativereact-router是路由的核心包,而其他兩個是基於特定環境的。如果你在開發一個網站,你應該使用react-router-dom,如果你在移動應用的開發環境使用React Native,你應該使用react-router-native

使用npm安裝react-router-dom

React Router 基礎

下面是路由的例子:

Router

像上面的例子,你需要一個元件和一些元件來建立一個基本的路由。由於我們建立的是一個基於瀏覽器的應用,我們可以從React Router API中使用這兩種型別的路由:

  1. <BrowserRouter>
  2. <HashRouter>

它們之間主要的區別,可以在它們所建立的URL明顯看出:

<BrowserRouter>在兩者中更為常用,原因是它使用了HTML5的history API來記錄你的路由歷史。而<HashRouter>則使用URL(window.location.hash)的hash部分來記錄。如果你想相容老式瀏覽器,你應該使用<HashRouter>

使用<BrowserRouter>元件包裹App元件。

index.js

注意:Router元件只能有一個子元素。子元素可以是HTML – 例如div – 也可以是一個react元件。

要讓React Router工作,你需要從react-router-dom庫引入相關的API。這裡,我在index.js引入了BrowserRouter,也從App.js引入了App元件。App.js,如你所猜想的,是React元件的入口。

上述程式碼給我們整個App元件建立了一個history例項。接下來正式介紹下history。

history

history是一個讓你輕鬆管理所有Javascript執行的會話記錄的Javascript庫。history提供了簡潔的API,讓你可以管理history堆疊,跳轉,確認跳轉,以及保持會話之間的狀態。 – 來自React 培訓文件

每個router元件建立了一個history物件,用來記錄當前路徑(history.location),上一步路徑也儲存在堆疊中。當前路徑改變時,檢視會重新渲染,給你一種跳轉的感覺。當前路徑又是如何改變的呢?history物件有history.push()history.replace()這些方法來實現。當你點選元件會觸發history.push(),使用則會呼叫history.replace()。其他方法 – 例如history.goBack()history.goForward() – 用來根據頁面的後退和前進來跳轉history堆疊。

接下來,我們談談Links和Routes

是React Router裡最重要的元件。若當前路徑匹配route的路徑,它會渲染對應的UI。理想來說,應該有一個叫path的prop,當路徑名跟當前路徑匹配才會渲染。

另一方面,用來跳轉頁面。可以類比HTML的錨元素。然而,使用錨連結會導致瀏覽器的重新整理,這不是我們想要的。所以,我們可以使用來跳轉至具體的URL,並且檢視重新渲染不會導致瀏覽器重新整理。

我們已經介紹了建立一個基本的路由需要的所有東西。讓我們試一個吧。

Demo 1: 基礎路由

src/App.js


我們在App.js裡定義了 Home,Category,和Products元件。儘管目前看起來沒問題,當元件變得越來越臃腫,最好將每個元件分成單獨的檔案。根據經驗,如果元件程式碼超過了10行,我通常會給它建立一個新的檔案。從第二個demo開始,我會將App.js裡面越來越多的元件分成單獨的檔案。

在App元件中,我們寫了路由跳轉的邏輯。 的路徑與當前路徑匹配,對應元件就會被渲染。對應渲染的元件傳給了第二個prop–component

在這裡,/同時匹配//category。因此,所有路由都匹配並被渲染。我們該如何避免呢?應該給 path='/'的路由傳遞exact= {true}props:

若只想要路由在路徑完全相同時渲染,你就可以使用exactprops。

巢狀路由

建立巢狀路由之前,我們需要更深入的理解如何執行。開始吧。

<Route>有三個可以用來定義要渲染內容的props:

  • component.在上面我們已經看到了。當URL匹配時,router會將傳遞的元件使用React.createElement來生成一個React元素。
  • render. 適合行內渲染。在當前路徑匹配路由路徑時,renderprop期望一個函式返回一個元素。
  • children.childrenprop跟render很類似,也期望一個函式返回一個React元素。然而,不管路徑是否匹配,children都會渲染。

Path and match

path用來標識路由匹配的URL部分。React Router使用了Path-to-RegExp庫將路徑字串轉為正規表示式。然後正規表示式會匹配當前路徑。

當路由路徑和當前路徑成功匹配,會生成一個物件,我們叫它match。match物件有更多關於URL和path的資訊。這些資訊可以通過它的屬性獲取,如下所示:

  • match.url.返回URL匹配部分的字串。對於建立巢狀的很有用。
  • match.path.返回路由路徑字串 – 就是。將用來建立巢狀的
  • match.isExact.返回布林值,如果準確(沒有任何多餘字元)匹配則返回true。
  • match.params.返回一個物件包含Path-to-RegExp包從URL解析的鍵值對。

現在我們完全瞭解了,開始建立一個巢狀路由吧。

Switch元件

在我們開始示例程式碼籤,我想給你介紹下元件。當一起使用多個時,所有匹配的routes都會被渲染。根據demo1的程式碼,我新增一個新的route來驗證為什麼很有用。

當URL為/products,所有匹配/products路徑的route都會被渲染。所以,那個path為:id的<Route>會跟著Products元件一塊渲染。設計就是如此。但是,若這不是你想要的結果,你應該給你的routes新增<Switch>元件。有<Switch>元件的話,只有第一個匹配路徑的子<Route>會渲染。

Demo 2: 巢狀路由

之前,我們給/, /category and /products建立了路由。但如果我們想要/category/shoes這種形式的URL呢?

src/App.js


不像React Router之前的版本,在版本4中,巢狀的最好放在父元素裡面。所以,Category元件就是這裡的父元件,我們將在父元件中定義category/:name路由。

src/Category.jsx


首先,我們給巢狀路由定義了一些Link。之前提到過,match.url用來構建巢狀連結,match.path用來構建巢狀路由。如果你對match有不理解的概念,console.log(match)會提供一些有用的資訊來幫助你瞭解它。

這是我們首次嘗試動態路由。不同於硬編碼路由,我們給pathname使用了變數。:name是路徑引數,獲取category/之後到下一條斜槓之間的所有內容。所以,類似products/running-shoes的路徑名會生成如下的一個params物件:

引數可以通過match.paramsprops.match.params來獲取,取決於傳遞哪種props。另外有趣的是我們使用了renderprop。render props非常適合行內函式,這樣不需要單獨拆分元件。

Demo 3: 帶Path引數的巢狀路由

我們讓事情變得再複雜一些,可以嗎?一個真實的路由應該是根據資料,然後動態展示。假設我們獲取了從服務端API返回的product資料,如下所示。

src/Products.jsx

我們需要根據下面這些路徑建立路由:

  • /products. 這個路徑應該展示產品列表。
  • /products/:productId.如果產品有:productId,這個頁面應該展示該產品的資料,如果沒有,就該展示一個錯誤資訊。

src/Products.jsx


首先,我們通過productsData.id建立一列,並把它儲存在linkList。路由從路徑字串根據匹配的對應產品id獲取引數。

你可能期望使用component = { Product }來替代行內render函式。問題是,我們不僅需要productsData,並順帶把剩餘prop也傳給Product元件。儘管你還有其他方法,不過我覺的這是最簡單的方法了。{...props}使用ES6的擴充套件運算子 將所有prop傳給元件。

這是Product元件的程式碼。

src/Product.jsx

find方法用來查詢陣列中物件的id屬性等於match.params.productId。如果product存在,productData就會展示,如果不存在,“Product不存在”的資訊就會被渲染。

保護式路由

最後一個demo,我們將圍繞保護式路由的技術進行討論。那麼,如果有人想進入/admin頁面,他們會被要求先登入。然而,在我們保護路由之前還需要考慮一些事情。

重定向

類似服務端重定向,會將history堆疊的當前路徑替換為新路徑。新路徑通過toprop傳遞。這是如何使用

如果有人已經登出了賬戶,想進入/admin頁面,他們會被重定向到/login頁面。當前路徑的資訊是通過state傳遞的,若使用者資訊驗證成功,使用者會被重定向回初始路徑。在子元件中,你可以通過this.props.location.state獲取state的資訊。

自定義路由

自定義路由最適合描述元件裡巢狀的路由。如果我們需要確定一個路由是否應該渲染,最好的方法是寫個自定義元件。下面是通過其他路由來定義自定義路由。

src/App.js

若使用者已登入,fakeAuth.isAuthenticated返回true,反之亦然。

這是PrivateRoute的定義。

src/App.js

如果使用者已登入,路由將渲染Admin元件。否則,使用者將重定義到 /login登入頁面。這樣做的好處是,定義更明確,而且PrivateRoute可以複用。

最後,下面是Login元件的程式碼:

src/Login.jsx


下面這行是物件的解構賦值的示例,es6的特性之一。

讓我們把所有片段拼湊到一塊,好嗎?這是我們使用React Router建立的應用最終效果:

Demo 4: 保護式路由

點選此檢視線上demo

總結

如你在本文中所看到的,React Router是一個幫助React構建更完美,更宣告式的路由庫。不像React Router之前的版本,在v4中,一切就“只是元件”。而且,新的設計模式也更完美的使用React的構建方式來實現。

在本次教程中,我們學到了:

  • 如何配置和安裝React Router
  • 基礎版路由,和一些基礎元件,例如,
  • 如何構建一個有導航功能的極簡路由和巢狀路由
  • 如何根據路徑引數構建動態路由

最後,我們還學習了一些高階路由技巧,用來建立保護式路由的最終demo。

感謝作者: Manjunath M

相關文章