1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        React Router 5 完整指南

        共 33392字,需瀏覽 67分鐘

         ·

        2021-06-06 19:02

        點(diǎn)擊上方 前端瓶子君,關(guān)注公眾號(hào)

        回復(fù)算法,加入前端編程面試算法每日一題群

        來源:vortesnail

        https://juejin.cn/post/6966242922278682632

        最近在搭建自己的網(wǎng)站時(shí),以前一直被自己認(rèn)為寫起來很簡(jiǎn)單的路由狠狠地給了我一巴掌,我既然怎么也想不到該怎么去合理地設(shè)計(jì)路由,痛定思痛,閱讀了很多文章及官方文檔,過程中也讀到了這一篇很好的基礎(chǔ)性文章,想翻譯下來向大家分享下!同時(shí),文中一些沒講到點(diǎn)上的,我都會(huì)進(jìn)行補(bǔ)充,歡迎大家閱讀與留言!

        另外,Twitter 已經(jīng)私信給原作者,得到了翻譯許可!

        React Router 是 React 社區(qū)最受歡迎的路由庫(kù),當(dāng)你需要在一個(gè)有多個(gè)頁(yè)面的 React 應(yīng)用程序中根據(jù) URL 來導(dǎo)航到對(duì)應(yīng)的頁(yè)面時(shí),就可以使用 React Router 來處理這個(gè)問題,它會(huì)使你的應(yīng)用的 UI 和 URL 保持同步。

        本教程將會(huì)向你介紹 React Router 5 以及你可以利用它而做到的一大堆事情。

        介紹

        我們都知道 React 是一個(gè)用于創(chuàng)建在客戶端進(jìn)行渲染單頁(yè)應(yīng)用(SPA)的流行庫(kù),在一個(gè) SPA 中可能有多個(gè)視圖(也可以叫頁(yè)面),但是與傳統(tǒng)的多頁(yè)應(yīng)用程序不同的是,瀏覽這些頁(yè)面時(shí)不會(huì)導(dǎo)致整個(gè)頁(yè)面被重新加載。我們希望的是這些頁(yè)面能夠在當(dāng)前頁(yè)面中進(jìn)行內(nèi)聯(lián)渲染,當(dāng)然了,如果我們習(xí)慣了多頁(yè)應(yīng)用程序,那么希望 SPA 中也要具有以下的功能:

        • 每個(gè)頁(yè)面都應(yīng)該有一個(gè)唯一指定該頁(yè)面的 URL,這是為了能讓用戶可以將 URL 加入書簽或直接輸入瀏覽器而訪問,比如 www.example.com/products。
        • 點(diǎn)擊瀏覽器的后腿和前進(jìn)按鈕都應(yīng)該如其如期工作。
        • 動(dòng)態(tài)生成的嵌套頁(yè)面最好也有一個(gè)自己的 URL,比如 www.example.com/products/shoes/101 ,其中 101 是產(chǎn)品 ID。

        路由是使瀏覽器的 URL 與頁(yè)面上正在展示的頁(yè)面保持同步的過程。React Router 讓你以聲明的方式處理路由,聲明式路由方法允許你控制應(yīng)用程序中的數(shù)據(jù)流,基本的使用方式就像下面一樣簡(jiǎn)單:

        <Route path="/about">
          <About />
        </Route>
        復(fù)制代碼

        這里簡(jiǎn)單提一下聲明式路由函數(shù)式路由分別長(zhǎng)啥樣:

        • 聲明式:<NavLink to='/products' /> 。
        • 函數(shù)式:histor.push('/products')

        你可以把 <Route> 組件放在任何你想渲染路由的地方,因?yàn)?<Route> ,<Link> 以及其它 React Router 的 APIs 都只是組件而已,所以你可以很容易地在 React 中啟動(dòng)和運(yùn)行路由。

        ?? 注意:有一個(gè)普遍的誤解,認(rèn)為 React Router 是由 Facebook 開發(fā)的官方路由解決方案。實(shí)際上,它只是一個(gè)第三方庫(kù),但因其設(shè)計(jì)和簡(jiǎn)單性而廣受歡迎。

        概覽

        本教程將會(huì)分為幾個(gè)小節(jié),首先我們會(huì)使用 npm 來安裝 React 和 React Router,接著就直接介紹 React Router 的基礎(chǔ)知識(shí)。你會(huì)看到根據(jù)不同知識(shí)點(diǎn)而寫的不同的代碼演示,本教程中涉及的例子有:

        • 基本的導(dǎo)航路由
        • 嵌套路由
        • 帶路徑參數(shù)的嵌套路由
        • 權(quán)限路由

        所有與構(gòu)建這些路由有關(guān)的概念都將在此過程中討論。另外,該項(xiàng)目的全部代碼可在 GitHub repo 上找到。 現(xiàn)在就讓我們搞起來吧!

        安裝 React Router

        請(qǐng)保證你電腦上安裝了 nodenpm ,然后利用 create-react-app 來創(chuàng)建一個(gè)新的 React 項(xiàng)目,我們直接使用 npx 來進(jìn)行項(xiàng)目的新建:

        npx create-react-app react-router-demo
        復(fù)制代碼

        npx 可以使你不需要全局安裝 create-react-app 就能創(chuàng)建 cra 項(xiàng)目。

        接下來切換到該項(xiàng)目目錄下:

        cd react-router-demo
        復(fù)制代碼

        React Router 庫(kù)包含三個(gè)包:react-router、react-router-dom 和 react-router-native 。路由操作相關(guān)的核心包是 react-router,而其他兩個(gè)是特定環(huán)境下使用的。如果你正在開發(fā)一個(gè) web 應(yīng)用,你應(yīng)該使用 react-router-dom,如果你在使用 React Native 開發(fā)移動(dòng)應(yīng)用,則應(yīng)該使用 react-router-native。 使用 npm 來安裝 react-router-dom

        npm install react-router-dom
        復(fù)制代碼

        然后執(zhí)行以下命令來啟動(dòng)本地服務(wù):

        npm run start
        復(fù)制代碼

        好了,你現(xiàn)在已經(jīng)有了一個(gè)安裝了 React Router 的 React 應(yīng)用,你可以在 http://localhost:3000/ 查看該應(yīng)用的運(yùn)行情況了。

        React Router 基礎(chǔ)知識(shí)

        現(xiàn)在讓我們熟悉一下 React Router 的基礎(chǔ)知識(shí),為了做到這一點(diǎn),我們將制作一個(gè)有三個(gè)獨(dú)立頁(yè)面的應(yīng)用程序:Home,Category 和 Products。

        Router 組件

        我們需要做的第一件事是將我們的 <App> 組件包裹在一個(gè) <Router> 組件中(由 React Router 提供)。由于我們正在建立的是一個(gè)基于瀏覽器的 web 應(yīng)用程序,我們可以使用 React Router API 中的兩種類型的路由:

        • BrowserRouter
        • HashRouter

        兩者主要區(qū)別在于他們創(chuàng)建的 URL 上:

        // <BrowserRouter>
        http://example.com/about
        // <HashRouter>
        http://example.com/#/about
        復(fù)制代碼

        <BrowserRouter> 在兩者中會(huì)更受歡迎些,因?yàn)樗褂玫氖?HTML5 History API 來保持應(yīng)用的頁(yè)面與 URL 同步,而 <HashRouter> 則使用的是 URL 的哈希部分(window.location.hash)。如果你的代碼運(yùn)行在不支持 History API 的傳統(tǒng)瀏覽器上,你應(yīng)該使用 <HashRouter> ,否則 <BrowserRouter> 對(duì)于大多數(shù)情況來說是更好的選擇。

        導(dǎo)入 BrowserRouter 組件并用其包裹 <App> 組件:

        // src/index.js

        import React from "react";
        import ReactDOM from "react-dom";
        import App from "./App";
        import { BrowserRouter as Router } from "react-router-dom";

        ReactDOM.render(
          <Router>
            <App />
          </Router>
        ,
          document.getElementById("root")
        );
        復(fù)制代碼

        在上面代碼中,我們?yōu)檎麄€(gè) <App> 組件創(chuàng)建了一個(gè) history 實(shí)例,等會(huì)向大家解釋這意味著什么。

        ?? 為了能讓大家更加明白這兩者有啥區(qū)別,我會(huì)在下面做一個(gè)簡(jiǎn)短的說明。

        <BrowserRouter><HashRouter> 區(qū)別

        BrowserRouter:

        BrowserRouter 要求服務(wù)端對(duì)發(fā)送的不同的 URL 都要返回對(duì)應(yīng)的 HTML,比如說現(xiàn)在有如下兩個(gè) URL 發(fā)送 GET 請(qǐng)求到服務(wù)端:

        http://example.com/home http://example.com/about
        復(fù)制代碼

        那么這個(gè)時(shí)候服務(wù)端拿到的是完整的 URL,這時(shí)候服務(wù)端就必須分別對(duì) /home/about 做處理并返回相應(yīng)的 HTML 來給到客戶端渲染。這個(gè)帶來的影響就是,如果你切換到某個(gè)服務(wù)端沒有做相應(yīng)處理的頁(yè)面路由,比如:

        http://example.com/article
        復(fù)制代碼

        如果你在 SPA 中寫了這部分路由要渲染的頁(yè)面,在頁(yè)面無刷新情況下跳轉(zhuǎn)是沒啥問題的。但是如果你直接在此路由下進(jìn)行頁(yè)面的刷新,就會(huì)得到一個(gè) 404。

        HashRouter

        HashRouter 在 URL 中使用哈希符號(hào)(#)來使服務(wù)端忽略 # 后面所有的 URL 內(nèi)容,比如你在瀏覽器地址欄中直接輸入以下 URL:

        http://example.com/#/home http://example.com/#/about
        復(fù)制代碼

        服務(wù)端拿到的只會(huì)是 http://example.com/ ,這樣服務(wù)端只需要對(duì)這個(gè)路由做處理并返回 HTML,然后后面的路由 /home/about 將全部交給客戶端(也就是我們的 SPA 應(yīng)用)來處理并渲染對(duì)應(yīng)的頁(yè)面。所以你在任意的路由進(jìn)行頁(yè)面的刷新都不會(huì)是 404。

        History 的小知識(shí)

        history 這個(gè)庫(kù)可以讓你在 JavaScript 運(yùn)行的任何地方都能輕松地管理回話歷史,history 對(duì)象抽象化了各個(gè)環(huán)境中的差異,并提供了最簡(jiǎn)單易用的的 API 來給你管理歷史堆棧、導(dǎo)航,并保持會(huì)話之間的持久化狀態(tài)?!?React Training 文檔

        每個(gè) <Router> 組件都會(huì)創(chuàng)建一個(gè) history 對(duì)象,它記錄了當(dāng)前的位置(history.location),還記錄了堆棧中以前的位置。在當(dāng)前位置發(fā)生變化時(shí),頁(yè)面會(huì)被重新渲染,于是你就有一種導(dǎo)航跳轉(zhuǎn)的感覺。

        那么如何改變當(dāng)前的位置呢?也就是說如何做到導(dǎo)航跳轉(zhuǎn)呢?這時(shí)候 history 的作用就來了,這個(gè)對(duì)象暴露了一些方法,比如 history.pushhistory.replace ,它們就可以拿來處理上面的問題。

        當(dāng)你點(diǎn)擊一個(gè) <Link> 組件時(shí),history.push 就會(huì)被調(diào)用,而當(dāng)你使用一個(gè) <Redirect> 組件時(shí),history.replace 就會(huì)被調(diào)用。其它的方法比如 history.goBackhistory.goForward 可以用來在歷史堆棧中回溯或前進(jìn)。

        LinkRoute 組件

        可以說 <Route> 組件是 React Router 中最重要的組件了,如果當(dāng)前的位置與路由的路徑匹配,就會(huì)渲染對(duì)應(yīng)的 UI。理想情況下,<Route> 組件應(yīng)該有一個(gè)名為 path 的屬性,如果路徑名稱與當(dāng)前位置匹配,它就會(huì)被渲染。

        <Link> 組件被用來在頁(yè)面之間進(jìn)行導(dǎo)航,它其實(shí)就是 HTML 中的 <a> 標(biāo)簽的上層封裝,不過在其源碼中使用 event.preventDefault 禁止了其默認(rèn)行為,然后使用 history API 自己實(shí)現(xiàn)了跳轉(zhuǎn)。我們都知道,如果使用 <a> 標(biāo)簽去進(jìn)行導(dǎo)航的話,整個(gè)頁(yè)面都會(huì)被刷新,這是我們不希望看到的,當(dāng)然,跳轉(zhuǎn)到首頁(yè)這種行為我倒是蠻喜歡用 <a> 標(biāo)簽的~

        所以我們使用 <Link> 組件來導(dǎo)航到一個(gè)目標(biāo) URL,可以在不刷新頁(yè)面的情況下重新渲染頁(yè)面。 現(xiàn)在我們已經(jīng)知道了所有要完成我們的 APP 所需要的知識(shí),接著更新 src/App.js ,如下所示:

        import React from "react";
        import { Link, Route, Switch } from "react-router-dom";

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

        );

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

        );

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

        );

        export default function App() {
          return (
            <div>
              <nav className="navbar navbar-light">
                <ul className="nav navbar-nav">
                  <li>
                    <Link to="/">Home</Link>
                  </li>
                  <li>
                    <Link to="/category">Category</Link>
                  </li>
                  <li>
                    <Link to="/products">Products</Link>
                  </li>
                </ul>
              </nav>
              {/* 如果當(dāng)前路徑與 path 匹配就會(huì)渲染對(duì)應(yīng)的組件 */}
              <Route path="/">
                <Home />
              </Route>
              <Route path="/category">
                <Category />
              </Route>
              <Route path="/products">
                <Products />
              </Route>
            </div>

          );
        }
        復(fù)制代碼

        在上面的 App.js 中我們定義了三個(gè)組件分別為 Home 、CategoryProducts 。雖然現(xiàn)在這樣做還算說得過去,但是當(dāng)一個(gè)組件內(nèi)的代碼變得很多時(shí),最好的方式是為每一個(gè)組件建立一個(gè)獨(dú)立的文件。就我的經(jīng)驗(yàn)來說,如果一個(gè)組件占用的代碼超過 10 行,我就會(huì)為它創(chuàng)建一個(gè)新的文件。所以從第二個(gè)演示開始,我將會(huì)為那些代碼過多而放在 App.js 中會(huì)顯得特別臃腫的組件單獨(dú)創(chuàng)建一個(gè)文件來存放。

        App 組件中我們已經(jīng)寫好了路由的邏輯,<Route>path 如果與當(dāng)前位置相匹配的話,對(duì)應(yīng)的組件也會(huì)被渲染。在以前,要被渲染的組件應(yīng)該作為 <Route> 組件的屬性傳入的,但是現(xiàn)在的版本只要作為 <Route> 的子組件就可以被正確渲染。

        在上面的路由設(shè)計(jì)中,/ 將會(huì)匹配 / 、/category 以及 /products ,這帶來的結(jié)果是會(huì)同時(shí)在頁(yè)面上渲染三個(gè)組件,即 Home 、CategoryProducts ,這不是我們所希望看到的。因此,我們可以通過傳入 exact 屬性給 <Route> 組件來避免這個(gè)問題出現(xiàn):

        <Route exact path="/">
          <Home />
        </Route>
        復(fù)制代碼

        所以如果你期望的是根據(jù)一個(gè)安全匹配的 path 去渲染對(duì)應(yīng)的組件,你就應(yīng)該考慮使用屬性 exact 了。

        嵌套路由

        如果想要使用嵌套路由,我們要更加深入地理解 <Route> 組件的工作方式,接下來我們一探究竟。 通過 React Router 官方文檔 可知,使用 <Route> 渲染一個(gè)頁(yè)面(或組件)的最佳方式是使用子元素方式,就像我們上面的演示一樣。然而,還是有一些其它的方式,這些方式是為了兼容在沒有引進(jìn) hooks 之前的早期版本的 React Router 構(gòu)建的 APP:

        • component :當(dāng) URL 匹配時(shí),React Router 會(huì)使用 React.createElement 從給定的組件創(chuàng)建一個(gè) React 元素。
        • render :能使你便捷的渲染內(nèi)聯(lián)組件或是嵌套組件,你可以給這個(gè)屬性傳入一個(gè)函數(shù),當(dāng)路由的路徑匹配時(shí)調(diào)用,返回一個(gè)元素。
        • children :與 render 屬性有些類似,它也是接收一個(gè)函數(shù),不同的是,無論現(xiàn)在 path 是否與當(dāng)前位置匹配,這個(gè)函數(shù)都會(huì)被執(zhí)行。

        路徑和匹配

        屬性 path 是用于識(shí)別路由應(yīng)該被匹配到的 URL 部分,它使用 path-to-regexp 庫(kù)將字符串形式的 path 轉(zhuǎn)換為一個(gè)正則表達(dá)式,然后將它與當(dāng)前的位置進(jìn)行匹配。

        如果路由的 path 與當(dāng)前位置完全匹配時(shí),一個(gè) match 對(duì)象 就會(huì)被創(chuàng)建,這個(gè)對(duì)象中有關(guān)于 URL 和路徑的更多信息,這些信息可以通過這個(gè)對(duì)象的屬性來進(jìn)行訪問,下面為大家列出有哪些屬性:

        • match.url :一個(gè)字符串(string),返回 URL 匹配的部分,這對(duì)于構(gòu)建嵌套的 <Link> 組件特別有用。
        • match.path :一個(gè)字符串(string),返回路由的 path ,即 <Route path=""> ,我們將使用它來構(gòu)建嵌套的 <Route> 組件。
        • match.isExact :一個(gè)布爾值(boolean),如果匹配時(shí)精確的,即沒有任何尾部字符,則返回 true 。
        • match.params :一個(gè)對(duì)象(object),返回的是從 URL 中解析出來鍵值對(duì)。

        屬性的隱式傳遞

        請(qǐng)注意,當(dāng)使用 component 屬性來渲染路由時(shí),match 、locationhistory 這些路由屬性是隱式地傳給被渲染的組件的。但當(dāng)使用比較新的路由渲染模式時(shí),情況有所不同。

        比如,以下面這個(gè)組件為例:

        const Home = (props) => {
          console.log(props);

          return (
            <div>
              <h2>Home</h2>
            </div>

          );
        };
        復(fù)制代碼

        以這種方式渲染路由:

        <Route exact path="/" component={Home} />
        復(fù)制代碼

        控制臺(tái)打印的日志:

        {
          history: { ... }
          location: { ... }
          match: { ... }
        }
        復(fù)制代碼

        但是現(xiàn)在如果以這種方式渲染路由:

        <Route exact path="/">
          <Home />
        </Route>
        復(fù)制代碼

        控制臺(tái)打印的日志將會(huì)是這樣:

        {}
        復(fù)制代碼

        可能你會(huì)覺得以這種方式來使用不太好,因?yàn)槲覀冊(cè)阡秩镜慕M件中拿不到路由屬性了。但是不用擔(dān)心,React v5.1 引入了幾個(gè) hooks,通過在組件內(nèi)部使用這些 hooks 可以助你訪問到上面隱式傳遞的任何路由屬性,這是一種新的管理路由狀態(tài)的方法,并在一定程度上使我們的組件更加整潔。 我將在本教程中使用其中的一些 hooks,但是如果你想要更深入地了解,可以查看 React Router v5.1 的發(fā)布公告。請(qǐng)注意,hooks 是在 React 的 16.8 版本中引入的,所以你至少需要在這個(gè)版本以上才能使用它們。

        Switch 組件

        在開始代碼演示之前,我想先向大家介紹一下 Switch 組件。當(dāng)多個(gè) <Route> 被一起使用時(shí),所有匹配到的路由都會(huì)被渲染,大家看下下面的代碼,我會(huì)向大家解釋為什么 <Switch> 是有用的:

        <Route exact path="/"><Home /></Route>
        <Route path="/
        category"><Category /></Route>
        <Route path="
        /products"><Products /></Route>
        <Route path="
        /:id">
          <p>This text will render for any route other than '/'</p>
        </Route>
        復(fù)制代碼

        如果 URL 是 /products ,那么 path/products/:id 的路由會(huì)一起在頁(yè)面渲染出來,這就是這樣設(shè)計(jì)的。然而,這種行為基本不可能是我們所期待的,所以才要用到 <Switch> ,有了 <Switch> ,只有第一個(gè)與當(dāng)前 URL 匹配到的子 <Route> 才會(huì)被渲染:

        <Switch>
          <Route exact path="/">
            <Home />
          </Route>

          <Route path="/category">
            <Category />
          </Route>

          <Route path="/products">
            <Products />
          </Route>

          <Route path="/:id">
            <p>This text will render for any route other than those defined above</p>
          </Route>

        </Switch>
        復(fù)制代碼

        path:id 部分用于動(dòng)態(tài)路由,它將匹配斜杠后面的任何東西,并且這個(gè)匹配到的值在被渲染的組件中是可以拿到的,我們會(huì)在下一節(jié)演示如何取這個(gè)值。

        現(xiàn)在我們知道了關(guān)于 <Route><Switch> 組件的一切,讓我們看看本節(jié)的主題嵌套路由的示例吧。

        動(dòng)態(tài)嵌套路由

        在上面的示例中我們創(chuàng)建了 / 、/category 、/products 路由,但是如果我們想要匹配一個(gè) /category/shoes 的路由咋辦呢?讓我們更新一波 src/App.js 的代碼:

        import React from "react";
        import { Link, Route, Switch } from "react-router-dom";
        import Category from "./Category";

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

        );

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

        );

        export default function App() {
          return (
            <div>
              <nav className="navbar navbar-light">
                <ul className="nav navbar-nav">
                  <li>
                    <Link to="/">Home</Link>
                  </li>
                  <li>
                    <Link to="/category">Category</Link>
                  </li>
                  <li>
                    <Link to="/products">Products</Link>
                  </li>
                </ul>
              </nav>

              <Switch>
                <Route path="/">
                  <Home />
                </Route>
                <Route path="/category">
                  <Category />
                </Route>
                <Route path="/products">
                  <Products />
                </Route>
              </Switch>
            </div>

          );
        }
        復(fù)制代碼

        你應(yīng)該注意到了,我已經(jīng)把 Category 組件獨(dú)立出來了,而我們的嵌套路由就在這個(gè)組件中去定義,那么現(xiàn)在就來創(chuàng)建 Category.js 吧!

        // src/Category.js

        import React from "react";
        import { Link, Route, useParams, useRouteMatch } from "react-router-dom";

        const Item = () => {
          const { name } = useParams();

          return (
            <div>
              <h3>{name}</h3>
            </div>

          );
        };

        const Category = () => {
          const { url, path } = useRouteMatch();

          return (
            <div>
              <ul>
                <li>
                  <Link to={`${url}/shoes`}>Shoes</Link>
                </li>
                <li>
                  <Link to={`${url}/boots`}>Boots</Link>
                </li>
                <li>
                  <Link to={`${url}/footwear`}>Footwear</Link>
                </li>
              </ul>
              <Route path={`${path}/:name`}>
                <Item />
              </Route>
            </div>

          );
        };

        export default Category;
        復(fù)制代碼

        在這里我們使用 useRouteMatch hook 來獲取上面我們說過的 match 對(duì)象。如前所述,match.url 為 URL 匹配的部分,用于構(gòu)建嵌套鏈接。match.path 為路由的 path ,用于構(gòu)建嵌套路由。 如果你覺得在 match 對(duì)象中的屬性有理解上的困難,沒關(guān)系,console.log(useRouteMatch()) 打印在控制臺(tái)仔細(xì)看看它的屬性的值是什么,你就大概能知道啥意思了。

        <Route path={`${path}/:name`}>
          <Item />
        </Route>
        復(fù)制代碼

        這就是我們對(duì)動(dòng)態(tài)路由的第一次嘗試,因?yàn)槲覀儧]有將路由寫死,而是在屬性 path 中使用了一個(gè)變量,:name 是一個(gè)路徑參數(shù),可以捕捉到 category/ 之后的所有內(nèi)容,直到遇到另外一個(gè)正斜杠(/)。因此,像 category/running-shoes 這樣的路徑名稱將會(huì)創(chuàng)建一個(gè) params 對(duì)象,如下所示:

        {
          name"running-shoes";
        }
        復(fù)制代碼

        為了在 <Item> 組件中訪問到這個(gè)值,我們使用 useParams hook ,它返回一個(gè) URL 參數(shù)的鍵值對(duì)的對(duì)象。 你可以在控制臺(tái)中打印下看看返回的到底是什么,那么現(xiàn)在 Category 應(yīng)該就會(huì)有三個(gè)子路由了。

        帶路徑參數(shù)的嵌套路由

        我們把這個(gè)例子在復(fù)雜化一點(diǎn),以便我們更好地去理解。在實(shí)際開發(fā)中,我們的路由必須具有處理數(shù)據(jù)并動(dòng)態(tài)展示它們的功能。假設(shè)有一些 API 返回的產(chǎn)品數(shù)據(jù),其格式如下:

        const productData = [
          {
            id: 1,
            name: "NIKE Liteforce Blue Sneakers",
            description:
              "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.",
            status: "Available",
          },
          {
            id: 2,
            name: "Stylised Flip Flops and Slippers",
            description:
              "Mauris finibus, massa eu tempor volutpat, magna dolor euismod dolor.",
            status: "Out of Stock",
          },
          {
            id: 3,
            name: "ADIDAS Adispree Running Shoes",
            description:
              "Maecenas condimentum porttitor auctor. Maecenas viverra fringilla felis, eu pretium.",
            status: "Available",
          },
          {
            id: 4,
            name: "ADIDAS Mid Sneakers",
            description:
              "Ut hendrerit venenatis lacus, vel lacinia ipsum fermentum vel. Cras.",
            status: "Out of Stock",
          },
        ];
        復(fù)制代碼

        假設(shè)我們還需要為以下的路徑創(chuàng)建路由:

        • /products :這應(yīng)該顯示一個(gè)產(chǎn)品列表。
        • /products/:productId :如果匹配到 :productId 那么就應(yīng)該顯示這個(gè)產(chǎn)品的數(shù)據(jù),如果沒有就顯示一個(gè)錯(cuò)誤信息。

        創(chuàng)建一個(gè)新文件 src/Products.js 文件,并添加以下代碼:

        import React from "react";
        import { Link, Route, useRouteMatch } from "react-router-dom";
        import Product from "./Product";

        const Products = ({ match }) => {
          const productData = [ ... ];
          const { url } = useRouteMatch();

          /* Create an array of `<li>` items for each product */
          const linkList = productData.map((product) => {
            return (
              <li key={product.id}>
                <Link to={`${url}/${product.id}`}>{product.name}</Link>
              </li>

            );
          });

          return (
            <div>
              <div>
                <div>
                  <h3>Products</h3>
                  <ul>{linkList}</ul>
                </div>
              </div>

              <Route path={`${url}/:productId`}>
                <Product data={productData} />
              </Route>
              <Route exact path={url}>
                <p>Please select a product.</p>
              </Route>
            </div>

          );
        };

        export default Products;
        復(fù)制代碼

        首先我們使用了 useRouteMatch 鉤子,并從 match 對(duì)象中拿到 URL ,然歐根據(jù)每個(gè)產(chǎn)品的 id 屬性來建立一個(gè) <Link> 組件的列表,并將其返回存儲(chǔ)到一個(gè) linkList 變量中。

        第一個(gè)路由使用 path 中的一個(gè)變量,它與產(chǎn)品 id 對(duì)應(yīng),當(dāng)匹配成功時(shí),我們就會(huì)渲染 <Product> 組件(我們馬上進(jìn)行定義),將我們的產(chǎn)品數(shù)據(jù)傳遞給它:

        <Route path={`${url}/:productId`}>
          <Product data={productData} />
        </Route>
        復(fù)制代碼

        注意到第二個(gè)路由中有一個(gè) exact 屬性,只有當(dāng) URL 是 /products 且其后面沒有任何路徑參數(shù)時(shí)才會(huì)渲染。

        OK,下面是 <Product> 組件的代碼,你只需要在 src/Product.js 創(chuàng)建這個(gè)文件:

        import React from "react";
        import { useParams } from "react-router-dom";

        const Product = ({ data }) => {
          const { productId } = useParams();
          const product = data.find((p) => p.id === Number(productId));
          let productData;

          if (product) {
            productData = (
              <div>
                <h3> {product.name} </h3>
                <p>{product.description}</p>
                <hr />
                <h4>{product.status}</h4>
              </div>

            );
          } else {
            productData = <h2> Sorry. Product doesn't exist </h2>;
          }

          return (
            <div>
              <div>{productData}</div>
            </div>

          );
        };

        export default Product;
        復(fù)制代碼

        find 方法用于在產(chǎn)品數(shù)組中搜索一個(gè) id 屬性與 match.params.productId 相同的對(duì)象。如果該產(chǎn)品存在,就會(huì)渲染對(duì)應(yīng)的數(shù)據(jù)。如果不存在,就會(huì)顯示 “產(chǎn)品不存在”的信息。

        最后,更新你的 <App> 組件,如下所示:

        import React from "react";
        import { Link, Route, Switch } from "react-router-dom";
        import Category from "./Category";
        import Products from "./Products";

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

        );

        export default function App() {
          return (
            <div>
              <nav className="navbar navbar-light">
                <ul className="nav navbar-nav">
                  <li>
                    <Link to="/">Home</Link>
                  </li>
                  <li>
                    <Link to="/category">Category</Link>
                  </li>
                  <li>
                    <Link to="/products">Products</Link>
                  </li>
                </ul>
              </nav>

              <Switch>
                <Route exact path="/">
                  <Home />
                </Route>
                <Route path="/category">
                  <Category />
                </Route>
                <Route path="/products">
                  <Products />
                </Route>
              </Switch>
            </div>

          );
        }
        復(fù)制代碼

        現(xiàn)在你就可以在瀏覽器中訪問你寫的這些路由了,如果你選擇“Products”,你會(huì)看到一個(gè)子菜單,并且顯示了產(chǎn)品的數(shù)據(jù)。 嘗試著好好理解下這個(gè)演示中的代碼,確保你要掌握這部分內(nèi)容。

        權(quán)限路由

        在如今大多數(shù)網(wǎng)站應(yīng)用中,只有登錄了的用戶才能訪問網(wǎng)站的某些部分,比如掘金登錄之后才會(huì)有進(jìn)入到個(gè)人主頁(yè)的入口。接下來這一節(jié),我會(huì)告訴大家如何去實(shí)現(xiàn)一個(gè)權(quán)限路由,也就是說如果有人試圖訪問 /admin ,他將會(huì)首先被要求登錄。

        然而,我們需要先了解 React Router 的幾個(gè)方面。

        <Redirect> 組件

        與服務(wù)端的重定向類似,React Router 的 Redirect component 將會(huì)用一個(gè)新的位置替換歷史棧中的當(dāng)前位置,新的位置是由 to 屬性來指向的。那么接下來我就會(huì)向大家介紹如何使用 <Redirect>

        <Redirect to={{ pathname: '/login', state: { from: location }}}
        復(fù)制代碼

        如果有人試圖在未登錄狀態(tài)下訪問 /admin 路由,他就會(huì)被重定向到 /login 路由,關(guān)于當(dāng)前位置的信息是由 state 屬性進(jìn)行傳遞的,這樣做是為了在用戶登錄成功之后,用戶又可以被重定向到他試圖訪問的路由頁(yè)面。

        自定義路由

        如果我們需要決定一個(gè)路由是否應(yīng)該被渲染,那么編寫一個(gè)自定義路由是個(gè)好辦法,接下來在 src 目錄下創(chuàng)建一個(gè)新文件 PrivateRoute.js ,并寫入以下代碼:

        import React from "react";
        import { Redirect, Route, useLocation } from "react-router-dom";
        import { fakeAuth } from "./Login";

        const PrivateRoute = ({ component: Component, ...rest }) => {
          const location = useLocation();

          return (
            <Route {...rest}>
              {fakeAuth.isAuthenticated === true ? (
                <Component />
              ) : (
                <Redirect to={{ pathname: "/login", state: { from: location } }} />
              )}
            </Route>

          );
        };

        export default PrivateRoute;
        復(fù)制代碼

        如你所見,在函數(shù)定義中,我們將接收到的 props 中拿到一個(gè) Component 還有一個(gè)剩余屬性 rest ,Component 將包含我們的 <PrivateRoute> 所保護(hù)的任何組件(在該例中為 Admin 組件),其余的屬性將會(huì)通過 rest 傳遞給 <Route>

        我們返回的是一個(gè) <Route> 組件,該組件會(huì)根據(jù)用戶是否登錄來決定是否渲染受到保護(hù)的組件,如果沒有登錄將會(huì)重定向到 /login 路由。這是由 fakeAuth.isAuthenticated 屬性決定的,這個(gè)屬性從 <Login> 組件中導(dǎo)入。 這種封裝的方法好處在于是聲明式的,而且 <PrivateRoute> 可被重復(fù)使用。

        實(shí)踐權(quán)限路由

        現(xiàn)在我們可以修改 src/App.js

        import React from "react";
        import { Link, Route, Switch } from "react-router-dom";
        import Category from "./Category";
        import Products from "./Products";
        import Login from "./Login";
        import PrivateRoute from "./PrivateRoute";

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

        );

        const Admin = () => (
          <div>
            <h2>Welcome admin!</h2>
          </div>

        );

        export default function App() {
          return (
            <div>
              <nav className="navbar navbar-light">
                <ul className="nav navbar-nav">
                  <li>
                    <Link to="/">Home</Link>
                  </li>
                  <li>
                    <Link to="/category">Category</Link>
                  </li>
                  <li>
                    <Link to="/products">Products</Link>
                  </li>
                  <li>
                    <Link to="/admin">Admin area</Link>
                  </li>
                </ul>
              </nav>

              <Switch>
                <Route exact path="/">
                  <Home />
                </Route>
                <Route path="/category">
                  <Category />
                </Route>
                <Route path="/products">
                  <Products />
                </Route>
                <Route path="/login">
                  <Login />
                </Route>
                <PrivateRoute path="/admin" component={Admin} />
              </Switch>
            </div>

          );
        }
        復(fù)制代碼

        正如你所見,我們?cè)谖募捻敳刻砑恿艘粋€(gè) <Admin> 組件,并在 <Switch> 組件下添加了一個(gè) <PrivateRoute> 組件。正如前面所說,如果用戶已經(jīng)登錄的話,這個(gè)自定義路由將會(huì)渲染的是 <Admin> 組件,否則,用戶會(huì)被重定向到 /login 。

        最后,這里是 Login 組件代碼:

        import React, { useState } from "react";
        import { Redirect, useLocation } from "react-router-dom";

        export default function Login() {
          const { state } = useLocation();
          const { from } = state || { from: { pathname"/" } };
          const [redirectToReferrer, setRedirectToReferrer] = useState(false);

          const login = () => {
            fakeAuth.authenticate(() => {
              setRedirectToReferrer(true);
            });
          };

          if (redirectToReferrer) {
            return <Redirect to={from} />;
          }

          return (
            <div>
              <p>You must log in to view the page at {from.pathname}</p>
              <button onClick={login}>Log in</button>
            </div>

          );
        }

        /* A fake authentication function */
        export const fakeAuth = {
          isAuthenticatedfalse,
          authenticate(cb) {
            this.isAuthenticated = true;
            setTimeout(cb, 100);
          },
        };
        復(fù)制代碼

        我們使用 useLocation hook 來訪問路由的 location 屬性,也就是從 state 屬性帶過來的。然后我們使用對(duì)象的解構(gòu)來獲取用戶在被要求登錄之前試圖訪問的 URL,這個(gè)這個(gè)值不存在,我們就設(shè)為 { pathname: "/" } 。

        然后我們使用 React 的 useState 鉤子來初始化一個(gè) redirectToReferrer 狀態(tài)為 false ,根據(jù)這個(gè)值來決定用戶是被重定向到他們想要訪問的路徑(也就是說用戶已經(jīng)登錄了),還是向用戶展示一個(gè)按鈕讓他們登錄。

        一旦按鈕被點(diǎn)擊,fakeAuth.authenticate 這個(gè)方法就會(huì)被執(zhí)行,它將 fakeAuth.isAuthenticated 設(shè)為 true ,并(在一個(gè)回調(diào)函數(shù)中)將 redirectToReferrer 狀態(tài)更新為 true ,這將導(dǎo)致組件重新渲染,用戶將被重定向。

        完整示例

        以下就是我們使用學(xué)到的東西做出來的最終 demo:

        Edit React Router Demo

        最后

        歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
        回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會(huì)很認(rèn)真的解答喲!
        回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
        回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
        如果這篇文章對(duì)你有幫助,在看」是最大的支持
         》》面試官也在看的算法資料《《
        “在看和轉(zhuǎn)發(fā)”就是最大的支持




        瀏覽 70
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            波多野结衣亚洲天堂 | 黄色免费国产视频 | 欧美极品另类 | 成人美女黄网站色大色费全看在线观看 | 精品免费国产一区二区三区四区的使用方法 | 亚洲无吗在线播放 | 亚洲欧美国产毛片在线 | 在线亚洲电影 | 91国内精品| 欧美精品aaa |