React Router Version 7 介紹
在網頁開發中,路由是 Web 應用程式的一項關鍵功能,它允許開發者可以透過路徑呈現不同的網頁內容給使用者,可能是一個 about、product 或是 article 等。然而,此篇章所要介紹的 React Router 是用於 React 函式庫的路由,並隨著時間推進,最新版本也已經到 Version 7。

前言
在網頁開發中,路由是 Web 應用程式的一項關鍵功能,它允許開發者可以透過路徑呈現不同的網頁內容給使用者,可能是一個 about、product 或是 article 等。然而,此篇章所要介紹的 React Router 是用於 React 函式庫的路由,並隨著時間推進,最新版本也已經到 Version 7。
React Router 是什麼?
React Router 是 React 的路由核心套件,而 React-Router-DOM 與 React-Router-Native 皆是基於此套件擴展建構的。
-
React-Router-DOM:適用於 Web 開發
-
React-Router-Native:適用於 App 開發
在前端網頁開發中許多的文章及教學影片當中,會看到設定 React Router 需要同時安裝 React Router 及 React Router DOM 這兩個套件,但最新版本的 React Router 已經將兩個套件整合為單一套件,因此,開發者只需要專注使用 React Router 並匯出相關 hook 進行路由控制即可。
react-router-dom 在 npm 說明它只是從 react-router 上重新匯出提供使用,並且更新後可以簡化匯入的路徑。

React Router 使用
安裝指令
npm i react-router
// or
pnpm add react-router
React Router 設定方式有 Framework mode、Data mode 及 Declarative mode 三種,此篇文章將介紹 Data 和 Declarative 兩種的設定及如何選擇。而 Framework mode 筆者目前尚未使用過,若有需要可參考官方文件。
| Framework mode 是在 v7.0 版本中新增的功能,所以在查看 v7 以下的版本時,會無法看到相關內容。
Declarative Mode (聲明模式)
- 傳統 React Router 路由的撰寫方式,使用
Routes及Route元件嵌套的方式建構路由。 - 適合用於小型專案,方便快速使用。
- 提供
BrowserRouter、HashRouter等路由元件。
需在 main.jsx/tsx 檔案中設定 BrowserRouter 或 HashRouter 路由模式。
// main.tsx
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router';
import App from './App.tsx';
createRoot(document.getElementById('root')!).render(
<BrowserRouter>
<App />
</BrowserRouter>,
);
使用 Routes 及 Route 嵌套方式設定路由,有時會區分前台與後台,它們兩者的 Layout 通常都有所不同,所以在下面範例會看到 FrontLayout 及 BackLayout,並在裡面嵌套各自需要的頁面元件。
Route 元件則需要給定 path 及 element 兩個屬性,path 是設定路由的路徑,element 則是設定當前路由被訪問時的元件。
// App.tsx
import { Routes, Route } from 'react-router';
import FrontLayout from './pages/FrontLayout';
import HomePage from './pages/HomePage';
import BackLayout from './pages/BackLayout';
import BackProductsPage from './pages/BackProductsPage';
function App() {
return (
<Routes>
<Route path="/" element={<FrontLayout />}>
<Route index element={<HomePage />} />
</Route>
<Route path="/back" element={<BackLayout />}>
<Route path="products" element={<BackProductsPage />}></Route>
</Route>
</Routes>
);
}
export default App;
從 react-router 引入 NavLink Component,並透過手動方式將頁面路徑添加到 Header Component 中
NavLink component 需要設定 to 屬性,這個屬性是告訴該按鈕要導航到哪個路徑。
// header.tsx
import { NavLink } from 'react-router';
export default function Header() {
return (
<header>
<nav>
<NavLink to="/">Home</NavLink>
<NavLink to="/about">About</NavLink>
<NavLink to="/products">Products</NavLink>
</nav>
</header>
);
}
Data Mode (資料模式)
React Router v6.4+新增的路由方式,如果讀者有在寫 Vue 的話,他跟 vue-router 的寫法類似。
- 官方推薦 Web 應用程式使用 Data mode,它提供更強大的功能。
- 提供
createBrowserRouter、createHashRouter等函式創建路由。 - 適合用於中大型專案,方便集中管理及擴展。
- 新增多項 Data API,如
loader、action、fetchers等。
需創建一支檔案用來管理整個網站的路由,通常會搭配新增 router 資料夾,並將設定路由的檔案命名為 index.jsx/tsx,這樣在引入 router 檔案時,可以簡化一些路徑。
從 react-router 引入 createBrowserRouter 或 createHashRouter 函式,並傳入一個陣列包物件的格式,該物件包含許多屬性,下面條列說明幾個重要的屬性。
path:設定路由的路徑。element:設定該路由被訪問時的渲染元件。errorElement:設定當路由發生錯誤時需要渲染的元件。children:用於設定子路由的屬性,該值也是陣列包物件的格式,並傳入path、element等屬性。handle:用於設定自定義的參數。下面範例使用該屬性設定該路由的名稱及是否顯示。
// router/index.tsx
import { createBrowserRouter } from 'react-router';
import FrontLayout from '../pages/FrontLayout';
import HomePage from '../pages/HomePage';
import BackLayout from '../pages/BackLayout';
import BackProductsPage from '../pages/BackProductsPage';
import BackUsersIcon from '../components/icons/BackUsersIcon';
// 路由表定義
const router = createBrowserRouter([
{
path: '/',
element: <FrontLayout />,
errorElement: <FrontErrorPage />,
children: [
{
index: true,
element: <HomePage />,
handle: { name: 'Home', show: true },
},
{
path: 'products',
element: <ProductsPage />,
handle: { name: 'Products', show: true },
},
{
// 動態路由設定
path: 'products/:slug',
element: <SingleProductPage />,
handle: { name: 'Single Product', show: false },
},
],
},
{
path: '/back',
element: <BackLayout />,
children: [
{
path: 'products',
element: <BackProductsPage />,
handle: { name: 'Products', show: true, icon: <BackUsersIcon /> },
},
],
},
]);
export default router;
將設定好的 router 檔案引入 app 元件,告訴 app 此應用程式使用哪個 router 設定,並透過 createBrowserRouter 、 createHashRouter 等函式回傳的 router object 傳入給 RouterProvider 元件。
// App.tsx
import { RouterProvider } from 'react-router';
import router from './router';
function App() {
return <RouterProvider router={router} />;
}
export default App;
使用 router 回傳的 object 可以取用到完整的 routes,並且在 header 元件中使用動態渲染的方式將定義好的路由表渲染到畫面上。這裡可以搭配條件回傳一個新資料結構後渲染,下方的範例使用 filter 方法取得前台的路由。
// header.tsx
import { NavLink } from 'react-router';
import router from '../router';
export default function Header() {
const frontNavList = router.routes
.find((item) => item.path === '/')
?.children?.filter((item) => item.handle.show);
return (
<header>
<nav>
<div>
{frontNavList?.map((nav) => (
<NavLink
key={nav.path}
to={nav.path as string}
className={({ isActive }) =>
`hover:text-thirdary text-sm/6 font-bold ${isActive ? 'front-active' : ''}`
}
>
{nav.handle.name}
</NavLink>
))}
</div>
</nav>
</header>
);
}
Declarative vs. Data
| 特性 | Declarative mode | Data mode |
|---|---|---|
| 新舊程度 | 舊 | 新 |
| 使用方式 | 使用元件定義路由 | 使用函式傳入路由結構 |
| 適用場景 | 適用於簡單路由,小型專案 | 適用於複雜應用,中大型專案 |
| 方便性 | 低,無法直接動態渲染 | 高,定義好路由,可透過回傳物件動態渲染 |
對於筆者來說,筆者自己是喜歡使用新的 Data mode 的方式來開發,因為可以把它們的職責、檔案區分開來,並且在需要使用 Router 的資料時,可以直接從回傳的 Object 取得,透過資料操作、動態渲染的方式快速設定到畫面上。