你可能想知道 React Router 究竟做了什麼?它如何協助你建立 App?究竟什麼是路由?
如果你曾對其中任何一個問題抱持疑問,或是你只想深入了解路由的基本概念,那麼你來對地方了。這份文件詳細解釋 React Router 中路由背後的所有核心概念。
請不要讓這份文件讓你不知所措!對於日常使用,React Router 非常簡單。你不需要深入瞭解就能使用它。
React Router 唔只是將 URL 配對至函式或組件:它會建立一個完整的使用者介面來對應 URL,因此它可能包含比你習慣更多的概念。我們將詳細說明 React Router 的三個主要任務
首先,一些定義!在前後端框架中有很多關於路由的不同概念。有時,一個單字在某個脈絡中可能會與在其他脈絡中具有不同的意思。
以下是一些我們在談論 React Router 時經常使用的字詞。本指南的後續部分將更詳細地探討每個字詞。
網址 - 地址列中的網址。許多人會互換使用「網址」和「路由」這兩個術語,但這不是 React Router 中的路由,而只是一個網址。
位置 - 這是基於內建瀏覽器 window.location
物件的 React Router 特有物件。它代表「使用者的所在位置」。它大部分是網址的物件表示形式,但比那略為多一點。
位置狀態 - 與 位置 一起保留但未編碼在 網址 中的值。很像雜湊或搜尋參數(編碼在網址中的資料),但隱藏地儲存在瀏覽器的記憶體中。
歷史記錄堆疊 - 當使用者瀏覽時,瀏覽器會在堆疊中追蹤每個 位置。如果你在瀏覽器中按住返回按鈕,你可以看到瀏覽器的歷史記錄堆疊就在那裡。
用戶端路由 (CSR) - 一般 HTML 文件可以連結到其他文件,而瀏覽器本身會處理 歷史記錄堆疊。用戶端路由讓開發人員可以操控瀏覽器歷史記錄堆疊,而無需對伺服器提出文件要求。
歷史記錄 - React Router 可以用來訂閱 網址 變更的物件,並提供 API 用於以程式化方式操控瀏覽器 歷史記錄堆疊。
歷史記錄動作 - POP
、PUSH
或 REPLACE
之一。使用者可以因以下三個原因之一到達 網址:將新項目加入歷史記錄堆疊時會產生推入動作(通常是點選連結或程式設計師強制導覽)。取代動作類似,只不過它會取代堆疊中的目前項目,而不是推入新的項目。最後,當使用者在瀏覽器工具列中點選返回或前進按鈕時會發生彈出動作。
路徑模式 - 這些看似網址,但有些特殊字元用於將網址與路由相符,例如動態片段 ("/users/:userId"
) 或星號片段 ("/docs/*"
)。它們並非網址,它們是 React Router 將相符的模式。
動態片段 - 路徑模式的區段為動態,表示可以相符區段中的任何值。例如模式 /users/:userId
將相符網址,例如 /users/123
路由 - 有狀態、頂層的元件,它讓所有其他元件和掛勾都能運作。
路由組態 - 路由物件樹,將根據目前的所在位置排序和相符 (包含巢狀),建立路由相符分支。
路由 - 物件或路由元素,形狀通常為「{ 路徑, 元素 }
」或「<Route 路徑 元素>
」。「路徑
」是路徑模式。當路徑模式相符目前的網址時,元素將被呈現。
路由元素 - 或「<Route>
」。它的屬性會被讀取,由「<Routes>
」建立路由,否則不會進行任何動作。
巢狀路由 - 由於路由可以有子代,且每個路由會定義網址的一部分透過片段,因此,單一網址可以在樹狀結構中相符多個路由的巢狀「分支」。這能透過出口、相對連結等功能自動建立排版巢狀。
相對連結 - 不以「/
」開頭的連結會繼承它在其中被呈現的最接近路由。這能更輕鬆連結到更深層的網址,而不用知道完整路徑並建置它。
相符 - 當路由相符網址時,會儲存資訊的物件,例如相符的網址參數和路徑名稱。
相符 - 一組相符目前所在位置的路由 (或路由組態分支)。此結構能建立巢狀路由。
父路由 - 含有子路由的路由。
出口 - 元件,在相符中呈現下一個相符。
索引路由 - 沒有路徑的子路由,在父路由的出口中,在父路由的網址中呈現。
版面路由 - 沒有路徑的父路由,專門用於在特定版面內群組子路由。
在 React Router 執行任何動作之前,它必須能夠訂閱瀏覽器 歷程堆疊 中的變更。
使用者瀏覽時,瀏覽器會維護自己的歷程堆疊。這就是「回上一頁」和「前往下一頁」按鈕運作的方式。在傳統網站(沒有 JavaScript 的 HTML 文件)中,只要使用者按下連結、提交表單或點選「回上一頁」或「前往下一頁」按鈕時,瀏覽器就會向伺服器發出請求。
例如,考慮下列使用者的操作:
/dashboard
/accounts
/customers/123
/dashboard
歷程堆疊會變為以下情況,其中 **粗體** 字體標示為目前的 URL 項目
/dashboard
/dashboard
, /accounts
/dashboard
, /accounts
, /customers/123
/dashboard
, /accounts
, /customers/123
/dashboard
, /accounts
, /dashboard
藉由使用**用戶端路由**,開發人員能夠以程式化方式處理瀏覽器的 歷程堆疊。例如,我们可以撰寫類似的程式碼,變更 URL,但沒有瀏覽器的預設行為向伺服器發出請求。
<a
href="/contact"
onClick={(event) => {
// stop the browser from changing the URL and requesting the new document
event.preventDefault();
// push an entry into the browser history stack and change the URL
window.history.pushState({}, undefined, "/contact");
}}
/>
window.history.pushState
這段程式碼會變更 URL,但不會對使用者介面產生任何影響。我們需要再撰寫一些能變更某些狀態的程式碼,才能讓使用者介面變更為聯絡人頁面。問題在於瀏覽器不會提供「監控 URL」並訂閱類似變更的方式給我們。
嗯,這並不完全正確。我們可以透過 pop 事件來監控 URL 的變更。
window.addEventListener("popstate", () => {
// URL changed!
});
但這只會在使用者按下「回上一頁」或「前往下一頁」按鈕時觸發。當程式設計師呼叫 window.history.pushState
或 window.history.replaceState
時,並沒有對應的事件。
這就是專屬於 React Router 的 history
物件發揮作用的地方。它提供了一種「監控 URL」變更的方式,不論 歷程操作 是**push**(推入堆疊)、**pop**(彈出堆疊)或**replace**(取代堆疊)。
let history = createBrowserHistory();
history.listen(({ location, action }) => {
// this is called whenever new locations come in
// the action is POP, PUSH, or REPLACE
});
應用程式不需要設定自己的歷史物件,那是 <Router>
的工作。它會設定其中一個物件,訂閱 歷史堆疊 的變更,最後在 URL 變更時更新狀態。這會導致應用程式重新呈現並顯示正確的 UI。它需要放入狀態中的唯一事項是 位置
,其餘所有項目都會由此單一物件運作。
瀏覽器在 window.location
上有一個位置物件。它會告訴你關於 URL 的資訊,但也有某些變更它的方法
window.location.pathname; // /getting-started/concepts/
window.location.hash; // #location
window.location.reload(); // force a refresh w/ the server
// and a lot more
window.location
React Router 沒有使用 window.location
,而是有一個基於 window.location
設計的 位置 概念,但它更簡單。它看起來像這樣
{
pathname: "/bbq/pig-pickins",
search: "?campaign=instagram",
hash: "#menu",
state: null,
key: "aefz24ie"
}
前三項: { pathname, search, hash }
完全就像 window.location
。如果你只新增這三項,就會取得使用者在瀏覽器中看到的 URL
location.pathname + location.search + location.hash;
// /bbq/pig-pickins?campaign=instagram#menu
最後兩項, { state, key }
,是 React Router 特定的。
位置路徑名稱
這是 URL 中來源之後的部分,因此對於 https://example.com/teams/hotspurs
路徑名稱是 /teams/hotspurs
。這是路線比對的位置唯一部分。
位置查詢
許多人都使用許多不同的術語來表示 URL 的這個部分
在 React Router 中,我們稱它為「位置查詢」。不過,位置查詢是 URLSearchParams
的序列化版本。因此,有時我們也可能會稱它為「URL 查詢參數」。
// given a location like this:
let location = {
pathname: "/bbq/pig-pickins",
search: "?campaign=instagram&popular=true",
hash: "",
state: null,
key: "aefz24ie",
};
// we can turn the location.search into URLSearchParams
let params = new URLSearchParams(location.search);
params.get("campaign"); // "instagram"
params.get("popular"); // "true"
params.toString(); // "campaign=instagram&popular=true",
精確來說,請將序列化的字串版本指稱為「查詢」,而將剖析的版本指稱為「查詢參數」,但當精確性不重要時,通常會交替使用這些術語。
位置雜湊
URL 中的雜湊表示 當前頁面 上的捲動位置。在 window.history.pushState
API 導入之前,網頁開發人員專門使用 URL 的雜湊部分進行用戶端路由,它是我們可以操作的唯一部分而不會對伺服器進行新的請求。然而,今天我們可以使用它來達到其設計目的。
位置狀態
你可能想知道為什麼 window.history.pushState()
API 稱為「推送狀態」。狀態?我們不只是變更 URL 而已嗎?它不應該是 history.push
嗎?好,當 API 設計時,我們不在現場,所以我們不知道為什麼「狀態」會成為重點,但它畢竟是瀏覽器很酷的功能。
瀏覽器讓我們可以透過傳遞值給 pushState
來保留關於導覽的資訊。當使用者按一下「返回」時,history.state
上的值會變更為之前「推入」的任何內容。
window.history.pushState("look ma!", undefined, "/contact");
window.history.state; // "look ma!"
// user clicks back
window.history.state; // undefined
// user clicks forward
window.history.state; // "look ma!"
React Router 利用這個瀏覽器功能,對其進行一些抽象化處理,並將值顯示在 `location` 中,而不是 `history`
您可以想像 `location.state`,就像 `location.hash` 或 `location.search`,只不過不是將值放在 URL 中,而是加以隱藏起來,就像 URL 中的超秘密部分,只有程式設計師會知道
`location state` 有幾個很好的使用案例
您有兩種方法可以設定 `location state`:在 `<Link>` 或 `navigate`
<Link to="/pins/123" state={{ fromDashboard: true }} />;
let navigate = useNavigate();
navigate("/users/123", { state: partialUser });
而在下一個頁面中,您可以使用 `useLocation` 來存取
let location = useLocation();
location.state;
位置金鑰
每個位置都會取得一個獨一無二的金鑰。這在進階案例中很有用,例如,基於位置的捲軸管理、用戶端資料快取等等。由於每個新位置都會取得一個獨一無二的金鑰,因此您可以建立在純粹物件、`new Map()` 甚至 `locationStorage` 中儲存資訊的抽象化處理方式
例如,一個非常基本的用戶端資料快取可以根據位置金鑰(以及擷取的 URL)儲存值,當使用者點選返回時,則略過撷取資料的程序
let cache = new Map();
function useFakeFetch(URL) {
let location = useLocation();
let cacheKey = location.key + URL;
let cached = cache.get(cacheKey);
let [data, setData] = useState(() => {
// initialize from the cache
return cached || null;
});
let [state, setState] = useState(() => {
// avoid the fetch if cached
return cached ? "done" : "loading";
});
useEffect(() => {
if (state === "loading") {
let controller = new AbortController();
fetch(URL, { signal: controller.signal })
.then((res) => res.json())
.then((data) => {
if (controller.signal.aborted) return;
// set the cache
cache.set(cacheKey, data);
setData(data);
});
return () => controller.abort();
}
}, [state, cacheKey]);
useEffect(() => {
setState("loading");
}, [URL]);
return data;
}
在初始呈現時和 歷史記錄堆疊變更時,React Router 會將 位置 與您的 路由設定 進行配對,以找出要呈現的一組 配對
路由設定是一個 路由 樹狀結構,如下所示
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path=":teamId/edit" element={<EditTeam />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
<Route element={<PageLayout />}>
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
<Route path="contact-us" element={<Contact />} />
</Routes>
會遞迴處理 `<Routes>` 組件中的 `props.children`,去掉其屬性,並產生如下物件
let routes = [
{
element: <App />,
path: "/",
children: [
{
index: true,
element: <Home />,
},
{
path: "teams",
element: <Teams />,
children: [
{
index: true,
element: <LeagueStandings />,
},
{
path: ":teamId",
element: <Team />,
},
{
path: ":teamId/edit",
element: <EditTeam />,
},
{
path: "new",
element: <NewTeamForm />,
},
],
},
],
},
{
element: <PageLayout />,
children: [
{
element: <Privacy />,
path: "/privacy",
},
{
element: <Tos />,
path: "/tos",
},
],
},
{
element: <Contact />,
path: "/contact-us",
},
];
事實上,您可以使用 `useRoutes(routesGoHere)` 這個勾子函式,來取代 `<Routes>`。這正是 `<Routes>` 在做的事
如您所見,路由可以定義多個 區段,例如 `:teamId/edit`,或只定義一個,例如 `:teamId`。`<Routes>` 組件會將 路由設定 中沿著分支的所有路徑段加在一起,為路由建立最終的 路徑格式
請注意 :teamId
區段。就是所謂 動態區段 路徑樣式,表示不會靜態比對 URL (實際字元) 而會動態比對。任何值都可以填入 :teamId
。無論是 /teams/123
或 /teams/cupcakes
都會比對成功。我們稱已剖析值為 URL 參數。因此,在此情況下,我們的 teamId
參數會是 "123"
或 "cupcakes"
。我們會在 顯示 區段說明如何在應用程式中使用這些參數。
如果我們加總 路由組態 所有分支中的所有區段,最後會得出以下路徑樣式,我們的應用程式會回應這些路徑樣式
[
"/",
"/teams",
"/teams/:teamId",
"/teams/:teamId/edit",
"/teams/new",
"/privacy",
"/tos",
"/contact-us",
];
這正是事情變得非常有趣的地方。考慮 URL /teams/new
。清單中哪一個樣式比對得到該 URL?
沒錯,有兩個!
/teams/new
/teams/:teamId
此時 React Router 必須做出決定,因為答案只能有一個。許多路由器(包括客戶端路由器和伺服器端路由器)會依照定義路徑樣式的順序處理這些樣式。第一個比對成功的就會獲勝。在此情況下,我們會比對 /
並顯示 <Home/>
元件。這絕對不是我們想要的結果。此類路由器要求我們將路由排序得完美無缺,才能得到預期的結果。React Router 的運作方式一直到 v6 都是如此,但現在它聰明多了。
查看那些樣式時,您憑直覺就知道我們想要將 /teams/new
比對為 URL /teams/new
。這是完美的比對!React Router 也知道。在比對時,它會根據區段數量、靜態區段、動態區段、星號樣式等,對您的路由進行評比,然後選擇最具體的比對。您再也不用考慮路由排序的問題了。
您可能已經注意到稍早出現的奇怪路由
<Route index element={<Home />} />
<Route index element={<LeagueStandings />} />
<Route element={<PageLayout />} />
它們甚至沒有路徑,怎麼可能是路由?這正是 React Router 中的「路由」一詞被用得非常寬鬆的地方。<Home/>
和 <LeagueStandings/>
是 索引路由,而 <PageLayout/>
是 版面配置路由。我們會在 顯示 區段說明它們的運作方式。兩者都沒有什麼比對工作要進行。
當路由比對得到 URL 時,會以 比對 物件表示。針對 <Route path=":teamId" element={<Team/>}/>
的比對會類似這樣
{
pathname: "/teams/firebirds",
params: {
teamId: "firebirds"
},
route: {
element: <Team />,
path: ":teamId"
}
}
pathname
儲存與此路由相符的 URL 部分(在本例中,為 URL 的所有部分)。params
儲存從任何 動態區段 中解析出的值(若有相符的動態區段)。請注意,參數物件的鍵會直接對應到區段的名稱::teamId
會變成 params.teamId
。
由於我們的路由是一棵樹,一個單一的 URL 可以與整棵樹的一個分支相符。考慮 URL /teams/firebirds
,它將會是以下的路由分支
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path=":teamId/edit" element={<EditTeam />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
<Route element={<PageLayout />}>
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
<Route path="contact-us" element={<Contact />} />
</Routes>
React Router 會從這些路由和 url 建立一個 matches 陣列,因此它可以呈現出與路由巢狀結構相符的巢狀 UI。
[
{
pathname: "/",
params: null,
route: {
element: <App />,
path: "/",
},
},
{
pathname: "/teams",
params: null,
route: {
element: <Teams />,
path: "teams",
},
},
{
pathname: "/teams/firebirds",
params: {
teamId: "firebirds",
},
route: {
element: <Team />,
path: ":teamId",
},
},
];
最後一個概念是呈現。假設你的應用程式入口如下所示
const root = ReactDOM.createRoot(
document.getElementById("root")
);
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
<Route element={<PageLayout />}>
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
<Route path="contact-us" element={<Contact />} />
</Routes>
</BrowserRouter>
);
讓我們再使用 /teams/firebirds
URL 作為範例。<Routes>
將會將 位置 與你的 路由設定 配合,取得一組 matches,然後呈現出如下的 React 元素樹
<App>
<Teams>
<Team />
</Teams>
</App>
每個在父路由元素內呈現的匹配都十分強大的抽象化。大多數網站及應用程式都具有以下特質:方塊包在方塊內、方塊再包在方塊內,每個都有導覽區段,且可以變更頁面上某一個子區段。
這個巢狀元素樹不會自動發生。<Routes>
會為你的第一個匹配項目呈現元素(在本例中,是 <App/>
)。下一個匹配項目的元素是 <Teams>
。為了呈現它,App
需要呈現一個 輸出位置。
function App() {
return (
<div>
<GlobalNav />
<Outlet />
<GlobalFooter />
</div>
);
}
Outlet
元件會隨時呈現下一個匹配。這表示 <Teams>
也需要一個輸出位置來呈現 <Team/>
。
如果 URL 是 /contact-us
,則元素樹就會變更為
<Contact />
因為連絡人表單不在主要的 <App>
路由之下。
如果 URL 是 /teams/firebirds/edit
,則元素樹就會變更為
<App>
<Teams>
<EditTeam />
</Teams>
</App>
輸出位置會將子項切換為與新子項相符的子項,但是父層配置會維持不變。這個技術十分細微,但是可以有效地清理元件。
請記住 /teams
的 路由設定
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
如果 URL 為 /teams/firebirds
,則元素樹會是
<App>
<Teams>
<Team />
</Teams>
</App>
但是,如果 URL 為 /teams
,則元素樹會是
<App>
<Teams>
<LeagueStandings />
</Teams>
</App>
聯盟排名怎麼會這樣冒出來?<Route index element={<LeagueStandings>}/>
是怎麼突然冒出來的?它甚至沒有路徑!這是因為它是一個 索引路由。索引路由會在父路由的 輸出位置 中,以父路由的路徑呈現。
以這種方式思考:如果您不在其中一個子路由的路徑中, 則 <Outlet>
會在 UI 中不顯示任何內容
<App>
<Teams />
</App>
如果所有小組都在左側的清單中,則空插座表示右邊有一個空白頁面! 您的 UI 需要些內容來填補空白:索引路由來救援。
思考索引路由的另一種方法是,它是父路由匹配但其子路由都未匹配時預設的子路由。
根據使用者介面,您可能不需要索引路由,但是如果父路由有任何類型的持續導航, 當使用者尚未按一下任一個項目時,您很可能需要一個索引路由來填補空白。
以下是我們尚未匹配的路由配置的一部分: /privacy
。 我們再次查看路由配置,並重點顯示匹配的路由
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path=":teamId/edit" element={<EditTeam />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
<Route element={<PageLayout />}>
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
<Route path="contact-us" element={<Contact />} />
</Routes>
並將會呈現出的結果元素樹會是
<PageLayout>
<Privacy />
</PageLayout>
<Outlet>
新增到版面中,您希望在其中顯示子路由元素。 使用 {children}
將無法達到預期效果。
PageLayout
路由確實很奇怪。 我們稱之為 版面路由,因為它根本不參與匹配(儘管其子路由有)。它的存在只是為了讓將多個子路由包裝在同一個版面中變得更簡單。 如果我們不允許這樣做,您將必須以兩種不同的方式處理版面:有時您的路由會為您執行,有時您會手動執行,而您的應用程式中到處都是大量的版面元件重複。
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path=":teamId/edit" element={<EditTeam />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
<Route
path="/privacy"
element={
<PageLayout>
<Privacy />
</PageLayout>
}
/>
<Route
path="/tos"
element={
<PageLayout>
<Tos />
</PageLayout>
}
/>
<Route path="contact-us" element={<Contact />} />
</Routes>
所以,是的,版面「路由」的語意有點愚蠢,因為它與 URL 匹配無關,但它實在太方便了,無法禁止。
當 URL 更改時,我們稱之為「導航」。 在 React Router 中有兩種導航方式
<Link>
導航
這是主要的導航方式。 瀏覽 <Link>
使用者可以在按一下時變更 URL。 React Router 將防止瀏覽器的預設行為並告訴 記錄 ,將一個新項目推入 記錄堆疊。 位置 發生變更,新的 匹配 會顯示。
但是,連結是可存取的,因為它們
<a href>
,因此符合所有預設的可存取性問題(例如鍵盤、焦點、SEO 等)嵌套的路由 不僅只處理瀏覽版面;它們還會啟用「相對連結」。 想想我們之前的 teams
路由
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
</Route>
<Teams>
元件可以顯示類似的連結
<Link to="psg" />
<Link to="new" />
連結到的完整路徑會是 /teams/psg
和 /teams/new
。它們繼承渲染所在的路徑。如此一來,您的路徑組件就不必真正知道應用程式內其他路徑的任何資訊。大量的連結只會遞進到多一個 區段。您可重新排列您的整個 路徑設定,而這些連結仍可能完全正常運作。這在您一開始建立網站並設計和版面四處變動時非常有幫助。
此函式傳回自 useNavigate
鉤子,並允許您,程式設計人員,在任何時候變更 URL。您可以在逾時時執行此操作
let navigate = useNavigate();
useEffect(() => {
setTimeout(() => {
navigate("/logout");
}, 30000);
}, []);
或是在提交表單後
<form onSubmit={event => {
event.preventDefault();
let data = new FormData(event.target)
let urlEncoded = new URLSearchParams(data)
navigate("/create", { state: urlEncoded })
}}>
就像 Link
,navigate
也與巢狀「至」值配合使用。
navigate("psg");
您應該有正當理由使用 navigate
,而不是 <Link>
。這讓我們感到非常難過
<li onClick={() => navigate("/somewhere")} />
除了連結和表單外,極少數互動應變更 URL,因為它會提高無障礙性和使用者預期的複雜性。
最後,應用程式將希望向 React Router 索取幾則資訊,以建構出完整的 UI。為此,React Router 有一堆鉤子
let location = useLocation();
let urlParams = useParams();
let [urlSearchParams] = useSearchParams();
讓我們一次從頭到尾說起!
您渲染您的應用程式
const root = ReactDOM.createRoot(
document.getElementById("root")
);
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
<Route element={<PageLayout />}>
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
<Route path="contact-us" element={<Contact />} />
</Routes>
</BrowserRouter>
);
<Routes>
遞迴了它的 子路徑 以建構 路徑設定、比對這些路徑與 位置,建立一些路徑 比對,並渲染第一個比對的路徑元素。
出口渲染路徑 比對 中的下一項比對。
使用者按下連結
連結呼叫 navigate()
歷程 變更 URL 並通知 <BrowserRouter>
。
<BrowserRouter>
重新渲染,從 (2) 開始!
就是這樣!我們希望本指南有助於您更深入瞭解 React Router 中的主要概念。