從 React Router v5 過(guò)渡到 v6

React-router 是 react js 中路由的標(biāo)準(zhǔn)庫(kù)。它允許 React 應(yīng)用程序的用戶在應(yīng)用程序的不同部分(組件)之間移動(dòng)。

react-router 團(tuán)隊(duì) 宣布將 在 2021 年底發(fā)布react-router 版本 6 (v6) 的穩(wěn)定版本,但由于一些重大的 API 更改,從 react-router 版本 5 (v5) 切換到 v6 可能會(huì)很困難. 在本文中,我們將介紹 v6 中的新功能以及如何將現(xiàn)有的 React 項(xiàng)目從 v5 升級(jí)到 v6。

要在我們的應(yīng)用程序中升級(jí) react-router 包的版本,我們導(dǎo)航到項(xiàng)目文件夾并運(yùn)行

npm install react-router-dom@[VERSION_NUMBER]
替換VERSION_NUMBER為我們要安裝的版本,或者如果我們想要最新版本,則替換為“ latest ”,如下所示:

npm install react-router-dom@6
或者

npm install react-router-dom@latest
請(qǐng)注意,我們必須連接到互聯(lián)網(wǎng)才能完成安裝,否則安裝將失敗。另外,請(qǐng)確保項(xiàng)目中的 react 版本是 v16.8 或更高版本,因?yàn)?react-router v6 嚴(yán)重依賴于 react v16.8 最初支持的鉤子

Switch 被替換為 Routes
v5 時(shí)代的第一個(gè)被替代的是Switch組件。該Switch組件用于包裝我們的路由,它確保每次只加載一個(gè)匹配的路由。但這在 v6 中不再存在。我們使用Routes組件來(lái)替換Switch。請(qǐng)注意,我們?nèi)匀恍枰獙?dǎo)入BrowserRouter包裝我們的應(yīng)用程序,就像在 v5 中所做的那樣。

在 v5 中,我們是這樣做:

import { BrowserRouter, Switch } from "react-router-dom";

function App() {
    return (
        <BrowserRouter>
            <div className="App">
                <Switch>
                    {" "}
                    {/* 路由Route在此定義 */}
                </Switch>
            </div>
        </BrowserRouter>
    );
}
export default App
但在 v6 中,我們將這樣做

import { BrowserRouter, Routes } from "react-router-dom";

function App() {
    return (
        <BrowserRouter>
            <div className="App">
                <Routes>
                    {" "}
                    {/* Switch 會(huì)被改成 Routes */}
                    {/* 路由Route在此定義 */}
                </Routes>
            </div>
        </BrowserRouter>
    );
}

export default App
Route組件使用更新
盡管該Route組件在 v6 中仍然保留一個(gè)位置,但我們定義它的使用方式與我們?cè)?v5 中的方式不同。我們將不再以 v5 中的任何方式放置我們想要渲染的組件,而是統(tǒng)一將其作為elementprop的值傳遞。

沒(méi)有exact配置
在 v5 中,不添加exact作為Route組件的props的話,如果 URL 以 path 關(guān)鍵字開(kāi)頭,則路徑將匹配,因?yàn)槠ヅ溥^(guò)程是從上到下的順序。但在 v6 中,我們將不再需要該exact配置,因?yàn)槁窂侥J狡ヅ渌惴ㄒ迅?,并且現(xiàn)在更加增強(qiáng)。

在 v5 中,我們這樣做了:

<Switch>
   {/* 三種Route組件使用定義 */}
   <Route path="/signup" component={Product} />
   {/* 或 */}
   {/* 這個(gè)方法允許我們將 props 傳遞給渲染的組件 */}
   <Route path="/games">
       <Product id={2} />
   </Route>
   {/* 或是通過(guò)render函數(shù) */}
   <Route path="/games" render={(props) => <Product {...props} />} />
</Switch>
在 v6 中,

<Routes>
   {" "}
   <Route path="/games" element={<Product />} />
   {/* 帶有props的渲染組件 */}
   <Route path="/movies" element={<Product id={200} category="shirt" />} />
</Routes>
Links 和 NavLinks
Link和NavLink組件仍然可以運(yùn)行在V6。Link組件使用與在 v5 的時(shí)候保持一樣,但使用NavLink組件時(shí),刪除了activeClassName和activeStyle prop。在 v5 中,activeClassNameprop 用于在鏈接激活后自動(dòng)將一些 CSS 類應(yīng)用于鏈接,同時(shí)activeStyle允許我們?cè)阪溄蛹せ顣r(shí)向鏈接添加內(nèi)部樣式。

但是在 v6 中,我們現(xiàn)在可以使用一個(gè)函數(shù)來(lái)獲取有關(guān)鏈接活動(dòng)狀態(tài)的信息。該函數(shù)的參數(shù)是一個(gè)具有屬性的對(duì)象isActive。此屬性在鏈接處于活動(dòng)狀態(tài)時(shí)為真,在非活動(dòng)時(shí)為假。isActive的值允許我們使用條件表達(dá)式來(lái)指示活動(dòng)樣式或類名。

在 v5 中,我們這樣做了:

import {NavLink} from “react-router-dom”

{/* … */}
<NavLink
   to="/product"
   style={{ color: "#689" }}
   activeStyle={{ color: "#3072c9" }}
   className="nav_link"
   activeClassName="active"
>
   Products
</NavLink>
但在 v6 中,我們將這樣做:

<NavLink
   to="/product"
   style={({ isActive }) => ({ color: isActive ? "#3072c9" : "#689" })}
   className={({ isActive }) => `link${isActive ? " active" : ""}`}
>
   Product
</NavLink>
Navigate替代Redirect
在 v5 中,我們使用該Redirect組件將一個(gè)頁(yè)面帶到另一個(gè)頁(yè)面,但它不再?gòu)?v6 中的 react-router-dom 導(dǎo)出。它已被Navigate組件替換。

在 v5 中,我們這樣做了:

<Route path="/faq">
   <Redirect to="/about" />
</Route>
<Route path="/about" component={About} />
但在 v6 中,我們將這樣做:

<Route path="/games" element={<Navigate to="/about" />} />;
<Route path="/games" element={<About />} />;
需要注意的是,如果我們只是按照Navigate上面代碼片段中的方式添加組件,它只會(huì)將導(dǎo)航到該路徑的導(dǎo)航推送到導(dǎo)航堆棧中,但是如果我們打算用新頁(yè)面替換當(dāng)前頁(yè)面,我們將 replace 屬性添加到Navigate組件中,如下所示:<Route path="/games" element={} />;

嵌套路由
顧名思義,嵌套路由是放置在另一個(gè)路由中的路由。它們用于在子組件中呈現(xiàn)更具體的信息。在 v6 中,我們將嵌套路由放置為父路由的子路由。然后我們引入Outlet組件,它是從渲染組件中的 react-router-dom導(dǎo)出的,用于指定我們希望嵌套信息顯示在哪里。Outlet 組件不是必需的,但它使代碼更清晰。在 v5 中,我們這樣做了:

import { useRouteMatch } from "react-router-dom";
function App() {
   return (
       <BrowserRouter>
           <Switch>
               <Route exact path="/about" component={About} />
               <Route path="/product" component={Product} />
           </Switch>
       </BrowserRouter>
   );
}

function Product() {
   let match = useRouteMatch();
   return (
       <div>
           <Switch>
               {/* match.path 返回父路由中指定的路徑。在這種情況下,它是“/product" */}
               <Route path={`${match.path}`}>
                   <AllProducts />
               </Route>
               {/* 匹配 /product/:id */}
               <Route path={`${match.path}/:id`}>
                   <ProductDetail />
               </Route>
           </Switch>

       </div>
   );
}
在 v6 中,我們這樣做:

import { Outlet } from "react-router-dom";

function App() {
   return (
       <Routes>
           <Route path="/about" element={<About />} />
           <Route path="/product" element={<Product />}>
               {/* 這里嵌套路由的路徑是相對(duì)于父路由的路徑的。 */}
               {/* 這里變成 "/product/" */}
               <Route path="/" element={<AllProducts />} />
               {/* 這里變成 "/product/:id" */}
               <Route path="/:id" element={<ProductDetail />} />

           </Route>
       </Routes>
   );
}

function Product() {
   return (
       <Container>
           <>
               <div>Product</div>
               {/* 父組件的其他內(nèi)容 */}
           </>
           {/* 這是嵌套信息開(kāi)始的地方 */}
           <Outlet />
       </Container>
   );
}
程序化導(dǎo)航
當(dāng)用戶因路徑上發(fā)生的事件(例如單擊按鈕、API 請(qǐng)求完成等)而被重定向時(shí),就會(huì)發(fā)生程序化導(dǎo)航。在 v5 中,我們可以使用useHistory鉤子來(lái)執(zhí)行以下操作:

import { useHistory } from "react-router-dom";

function Product() {
   const history = useHistory();

   const handleClick = () => {
       //這會(huì)將新路線推送到導(dǎo)航堆棧的頂部
       history.push("/new-route");

       //這會(huì)將當(dāng)前路線替換為導(dǎo)航堆棧中的新路由
       history.replace("/new-route");
   };

   return (
       <div>
           <button>點(diǎn)擊我重定向到新路由</button>
       </div>
   );
}
但是在 v6 中,useHistoryhook 被替換為useNavigatehook,并且我們以不同的方式使用它。

import { useNavigate } from "react-router-dom";

function Product() {
   const navigate = useNavigate();

   const handleClick = () => {
       //這會(huì)將新路線推送到導(dǎo)航堆棧的頂部
       navigate("/new-route");

       //這會(huì)將當(dāng)前路線替換為導(dǎo)航堆棧中的新路由
       navigate("/new-route", { replace: true });
   };

   return (
       <div>
           <button>點(diǎn)擊我重定向到新路由</button>
       </div>
   );
}
一件很酷的事情是我們可以在導(dǎo)航堆棧上任意前進(jìn)和后退。通過(guò)使用正數(shù)作為上述參數(shù)navigate(),路由會(huì)向前移動(dòng)該步數(shù)。負(fù)數(shù)向后做同樣的事情

// Goes forward
navigate(1)
// Goes forward twice
navigate(2)
// Goes backward
navigate(-1)
// Goes backward three times
navigate(-3)
刪除Prompt組件
Prompt如果有未保存的更改,v5 中的組件可防止意外離開(kāi)頁(yè)面。但是 react-router 團(tuán)隊(duì)并沒(méi)有將它包含在 v6 中,也沒(méi)有替代方案。因此,如果你需要該功能,你可以手動(dòng)實(shí)現(xiàn)它或返回到 v5。

除了不包括Prompt在當(dāng)前版本(v6)中,useBlocker和usePrompt都不起作用。react-router團(tuán)隊(duì)雖然在官方文檔中表示,他們目前正在努力將其添加回 v6,但不是針對(duì) 6.x 的第一個(gè)穩(wěn)定版本。

概括
讓我們強(qiáng)調(diào)一下我們所經(jīng)歷的變化。

Switch 組件替換為 Routes 組件。

如何放置 Route 的渲染組件的更改。

路由中沒(méi)有exact。

activeClassName和activeStyle不存在于NavLink組件了.

我們可以通過(guò)函數(shù)回調(diào)訪問(wèn) NavLink 組件的 isActive 狀態(tài)。

Redirect組件已替換為Navigate組件。

實(shí)現(xiàn)嵌套路由的一種更時(shí)尚的方式。

沒(méi)有Prompt組件

總之,如果你還沒(méi)有準(zhǔn)備好從 v5 或任何其他版本切換到 v6,你可以繼續(xù)使用它安裝以前的版本。

npm install react-router-dom@[VERSION_NUMBER]
但是,你將錯(cuò)過(guò) v6 附帶的一些好東西,包括但不限于:

增強(qiáng)的路徑模式匹配算法。
根據(jù)Bundlephobia,體積大小減少了 60%
我相信我們能夠成功地切換到 react-router v6 并停止使用 Switch 組件(雙關(guān)語(yǔ)非常有意)


作者:xiaobin1213

歡迎關(guān)注微信公眾號(hào) :前端晚間課

更多文章,收錄于小程序-互聯(lián)網(wǎng)小兵