前面我們已經認識了基本的 React Router,接著我們要認識另一個觀念,也就是巢狀路由,而巢狀路由在實戰開發上是很常見的,因此這一篇我們就必須要來認識一下。
在開始介紹巢狀路由(Nested Routes)之前,我們要先認識一下 React Router 中的另一個東西也就是 Link
,在前面的練習結尾處,我們用了一個很蠢的方式去切換路由,也就是直接透過瀏覽器的網址列輸入 /todolist
到 ToDoList 頁面,不得不說這種方式很蠢,你也不可能讓使用者用這種方式去切換路由對吧?那...實際上來講我們該怎麼做才對呢?
正確來講我們要使用 React Router 的 Link
來切換我們想要到達的頁面,所以這邊就先打開 main.jsx,先在這邊暫時加入 List
即可。
使用方式很簡單直接將 List
放在 Routes
外面就可以了,而這邊我稍微做了一下簡單版的導覽列
import React from 'react'
import { HashRouter, Route, Routes, Link } from "react-router-dom";
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import ToDoList from "./TodoList";
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<HashRouter>
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
<h1 className="mr-auto text-2xl">React TodoList</h1>
<ul className="flex">
<li className="mr-3">
<Link to="/" className="border p-3 hover:bg-indigo-600 duration-500">Home</Link>
</li>
<li>
<Link to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</Link>
</li>
</ul>
</nav>
<Routes>
<Route path="/" element={ <App /> } />
<Route path="/todolist" element={ <ToDoList /> } />
</Routes>
</HashRouter>
</React.StrictMode>
)
請注意 Link
必須放在 HashRouter
裡面,否則噴這個錯誤訊息
Uncaught Error: useHref() may be used only in the context of a Router component.
那麼拉回到剛剛講到一半的部分,上面我們新增了兩個 Link
,一個是首頁跟 ToDoList
頁面,接下來當你點上方任何一個連結都會發現下方畫面會跟著切換
我們可以看到 Link
的使用方式非常簡單,只需要戴上一個 to
參數並設定與 Route
中 Path
屬性相同名稱就可以成功到該頁面了。
有沒有覺得很像 Vue Router 的 router-link
呢?簡單讓你回憶一下 Vue Router 的導航切換方式。
<router-link to="/todolist">ToDoList</router-link>
回到巢狀路由的部分,我們會發現有一個問題存在,我們所有的頁面都是在 /
底下,可是實際開發來講不可能通通都在 /
,一定還會有 admin
、dashboard
等等路由,然後掛在這些路由底下,舉例來講我們預期 Url 會變化成以下
https://www.example.com/#/admin
https://www.example.com/#/admin/products
這時候你可能會想說...「那 Route
就這樣寫啊?有問題嗎?」
<Routes>
<Route path="/" element={ <App /> } />
<Route path="/todolist" element={ <ToDoList /> } />
<Route path="/admin" element={ <ToDoList /> } />
<Route path="/admin/products" element={ <ToDoList /> } />
</Routes>
確實這樣子寫是可以達到前面所提的效果,但是 admin
的頁面與 user
的頁面必定會有一些不同,例如 admin
有自己的導覽列與 user
必定是不同的,總不可能 user
可以看到 admin
的功能操作吧?如果你真的這樣做的話,那麼就會導致所有頁面都吃到同一種導覽列,因此這邊就會需要使用到巢狀路由來達到 Layout 的概念。
那麼我們該如何撰寫巢狀路由呢?其實很簡單,你只需要將 Route
包在 Route
中就可以了,如下
<Routes>
<Route path="/" element={ <App /> } >
<Route path="todolist" element={ <ToDoList /> } />
</Route>
<Route path="/admin" element={ <Admin /> } >
<Route path="products" element={ <AdminProducts /> } />
</Route>
</Routes>
接下來為了讓大家體感上更明顯一點,所以這邊請你將剛剛寫在 main.jsx 的導覽列
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
<h1 className="mr-auto text-2xl">React TodoList</h1>
<ul className="flex">
<li className="mr-3">
<Link to="/" className="border p-3 hover:bg-indigo-600 duration-500">Home</Link>
</li>
<li>
<Link to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</Link>
</li>
<li>
<Link to="/admin" className="border p-3 hover:bg-indigo-600 duration-500">Admin</Link>
</li>
<li>
<Link to="/admin/products" className="border p-3 hover:bg-indigo-600 duration-500">AdminProducts</Link>
</li>
</ul>
</nav>
全部移動到 App.jsx 中,並將後台頁面的 Link
改成 /admin
就好,另一個 /admin/products
則刪除,因此目前 App.jsx 呈現如下
import { Link } from "react-router-dom";
const App = () => {
return (
<>
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
<h1 className="mr-auto text-2xl">React TodoList</h1>
<ul className="flex">
<li className="mr-3">
<Link to="/" className="border p-3 hover:bg-indigo-600 duration-500">Home</Link>
</li>
<li className="mr-3">
<Link to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</Link>
</li>
<li className="mr-3">
<Link to="/admin" className="border p-3 hover:bg-indigo-600 duration-500">Admin</Link>
</li>
</ul>
</nav>
<h1>App</h1>
</>
)
}
export default App;
這時候你應該會發現畫面滿空的,而且你點 ToDoList
也不會出現在畫面上,這邊原因是因為還要額外加上一個東西叫做 <Outlet />
在 App.jsx,例如:放在 <h1>App</h1>
底下
import { Link, Outlet } from "react-router-dom";
...// 略過
<h1>App</h1>
<Outlet />
...// 略過
放上 <Outlet />
之後,你就可以看到畫面正常的切換了
當你點擊 Admin 時,你也會發現頁面就是很純粹的空白一片,不會有前台的 Navbar
出現,只會出現後台專用的 Navbar
(我有微調 Admin.jsx),而這就是共用 Layout
的概念,也是巢狀路由概念
範例程式碼:React Router Example
本文將會同步更新到我的部落格