這篇文章主要說明如何整合 react-router 來製作一個具有導覽功能的麵包屑(breadcrumb),也就是這個麵包屑可以根據當前使用者瀏覽的路由動態顯示出對應的名稱。由於會說明之所以這麼做的思路,因此篇幅較長;如果想要直接看如何使用這段程式碼,可以到 Github 上檢視
ReadMe,當中的說明較為精簡。。
這篇文章不會從頭開始說明 React 和 React Router 的使用,因此建議閱讀前應該具備基本的 React 和 React Router 知識,不然可能會看得相當吃力。
來看看怎麼做吧!
在這裡我們直接使用 create-react-app 來建立一個簡單的 React 專案,就稱作 react-router-breadcrumb:
$ create-react-app react-router-breadcrumb
$ cd react-router-breadcrumb
另外,需要使用到 react-router-dom 來幫我們建立路由:
$ npm install react-router-dom
接著就可以啟動專案,然後到 localhost:3000 即可看到預設的畫面:
$ npm run start
為了讓我們的畫面比較乾淨一些,就先直接套
Bootstrap 4 進來用,如果不想套的話也是可以,就是畫面會比較呆板一些。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
/>
<title>React App</title>
</head>
<body>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
</body>
</html>
接著把所有在 App.js 中預設的畫面都清掉,寫個「Hello React」確認沒問題就好:
import React, { Component } from 'react';
class App extends Component {
render() {
return <h1 className="text-primary">Hello React</h1>;
}
}
export default App;
畫面長這樣空空的,而且因為套了 Bootstrap 中 text-primary 的樣式,文字有變色,就代表有成功載入 Bootstrap 了:
清理完後就可以來建立所需要的頁面。
先把在這個專案中會使用的頁面建立起來,可以想像一個商城的結構大概是這樣,我們有三個外層的路由,分別是「首頁」、「書籍館」和「3C 商品館」,而 「3C 商品館」中又會細分出「手機館」、「桌機館」和「筆電館」,從這樣的結構可以看出,將會使用到嵌套式路由(Nested Routing):
- Home # 首頁
- Books # 書籍館
- Electronics # 3C 商品館
--- Mobile # 手機館
--- Desktop # 桌機館
--- Laptop # 筆電館
因為頁面(Page)的內容不是我們的重點,所以在本文中把所有的頁面組件(Page component)都寫在一支 pages.js 的檔案中。
import React from 'react';
const Home = () => {
return <h1 className="py-3">Home</h1>;
};
const Books = () => {
return <h1 className="py-3">Books</h1>;
};
const Electronics = () => {
return <h1 className="py-3">Electronics</h1>;
};
const Mobile = () => {
return <h3>Mobile Phone</h3>;
};
const Desktop = () => {
return <h3>Desktop PC</h3>;
};
const Laptop = () => {
return <h3>Laptop</h3>;
};
export { Home, Books, Electronics, Mobile, Desktop, Laptop };
再來我們先建立基本的路由,以便透過輸入網址連到這些頁面。
在 index.js 中,先載入 BrowserRouter 和 Switch:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Switch } from 'react-router-dom';
import './index.css';
import App from './App';
ReactDOM.render(
<BrowserRouter>
<Switch>
<App />
</Switch>
</BrowserRouter>,
document.getElementById('root')
);
在 App.js 中,把當初 create-react-app 建立但用不到的內容都砍掉,只需要指定不同的路由應該要對應到哪些頁面就好,對於 <Route /> 這個組件的概念不用想得太複雜,簡單理解成就是當瀏覽器網址列的 URL 和這個 path 相匹配到時,就會在「這個位置」顯示該 component:
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import { Index, Books, Electronics } from './pages';
class App extends Component {
render() {
return (
<div className="container">
{}
<Route path="/" exact component={Index} />
<Route path="/books" component={Books} />
<Route path="/electronics" component={Electronics} />
</div>
);
}
}
export default App;
這時候當你在網址列輸入 /, /books, /electronics,應該就能順利看到那些頁面。
對於 <Route /> 這個組件的概念不用想得太複雜,簡單來說就是當瀏覽器的 URL 和這個 path 相匹配到時,就會在「這個位置」載入該 component。
這時候因為還沒配置嵌套式路由的緣故,因此輸入 /electronics/mobile 時還不會找到相對應的頁面,依照同樣的概念,我們可以在 Electronics 這個 Page 中加入 <Route /> 組件,一旦當前瀏覽器網址列上的 URL 和 <Route /> 中的 path 相配對時,就會在「這個位置」顯示出所指定的頁面。
因此,在 pages.js 中的 Electronics 組件中,加上路由:
import { Switch, Route } from 'react-router-dom';
const Electronics = () => {
return (
<div>
<h1>Electronics</h1>
<Switch>
{}
<Route path="/electronics/mobile" component={Mobile} />
<Route path="/electronics/desktop" component={Desktop} />
<Route path="/electronics/laptop" component={Laptop} />
</Switch>
</div>
);
};
這時候當我們在輸入網址列 /electronics/mobile 時,也會出現相對應的畫面:
每一次都要從網址列輸入網址實在有點麻煩,既然路由都配置好了,先來做個導覽列方便使用吧。
為了方便示範,而且這個專佔中不會有太多的 React 組件,我們把在頁面中會套用到的組件都放在一隻叫做 components.js 的檔案中。
import React from 'react';
import { Link } from 'react-router-dom';
import logo from './logo.svg';
const Navbar = () => {
return (
<nav className="navbar navbar-expand-sm navbar-light bg-light">
<Link className="navbar-brand" to="/">
<img src={logo} alt="react-router-breadcrumb" width="30" height="30" />
</Link>
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarContent"
aria-controls="navbarContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon" />
</button>
<div className="collapse navbar-collapse" id="navbarContent">
<ul className="navbar-nav">
<li className="nav-item">
<Link className="nav-link" to="/">
Home
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/books">
Books
</Link>
</li>
<li className="nav-item dropdown">
<Link
className="nav-link dropdown-toggle"
to="/electronics"
id="navbarDropdownMenuLink"
role="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Electronics
</Link>
<div
className="dropdown-menu"
aria-labelledby="navbarDropdownMenuLink"
>
<Link className="dropdown-item" to="/electronics/mobile">
Mobile Phone
</Link>
<Link className="dropdown-item" to="/electronics/desktop">
Desktop PC
</Link>
<Link className="dropdown-item" to="/electronics/laptop">
Laptop
</Link>
</div>
</li>
</ul>
</div>
</nav>
);
};
export { Navbar };
接著在 <App /> 組件中載入 <Navbar /> 即可:
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import { Home, Books, Electronics } from './pages';
import { Navbar } from './components';
class App extends Component {
render() {
return (
<div className="container">
{}
<Navbar />
<Route path="/" exact component={Home} />
<Route path="/books" component={Books} />
<Route path="/electronics" component={Electronics} />
</div>
);
}
}
export default App;
到目前為止完成畫面差不多完成了:
到目前為止,已經可以根據 react-router 顯示出相對應的頁面。一般來說,這樣的路由配置是沒有問題的,但這樣做在製作麵包屑時會碰到一個問題,我們將無法知道每一個對應到的 path 它的麵包屑名稱是什麼,什麼意思呢?
例如,當 path 是 /electronics/desktop 時,希望麵包屑名稱會顯示「Desktop PC」;當 path 為 /electronics 時,麵包屑名稱則要顯示「Electronics」,這些麵包屑的名稱是無法直接從路由的 path 看出來的。
直覺上,透過 React Router 提供的 render 方法,我們可以把麵包屑的名稱當做 props 傳到對應的 Page 當中,像下面這樣:
<Route
path="/books"
render={(props) => <Books breadcrumbName="books" {...props} />}
/>
但這樣做會有個問題,以 /electronics/desktop 為例,當透過 props 把 breadcrumbName 傳到該組件中時,雖然到路由 /electronics/desktop 時我們可以取得這個 Page 的麵包屑名稱為 "Desktop PC",但是我們沒辦法知道 /electronics 的麵包屑名稱是什麼,然而,麵包屑需要顯示的樣子應該要會像這樣:
Home > Electronics > Desktop PC
因此直接透過 props 把 breadcrumbName 傳入頁面中似乎不能達到想要的功能。
定義好的路由表會長像這樣:
const breadcrumbNameMap = new Map([
['/', 'Home'],
['/books', 'Book'],
['/electronics', 'Electronics'],
['/electronics/mobile', 'Mobile'],
['/electronics/desktop', 'Desktop'],
['/electronics/laptop', 'Laptop']
]);
但這麼做的麻煩之處在於,每當我們要添加路由時,除了先透過 <Route path="/" component={Home} /> 撰寫好路由後,還需要把這個新的路由添加到路由表中,如果忘了加,麵包屑就出不來。
沒辦法寫一次就直接套用覺得有些麻煩,因此後來我們決定不這麼用。
為了不要額外建立一個路由表,勢必要把路由所對應到的麵包屑名稱,集中設定在一個地方,而這種集中式路由管理的方式可以方便我們在一個地方把路由和麵包屑名稱都寫好。
於是我們要來重新組織一下路由,把它變成集中式的路由設定,並且可以把每一路由對應到的麵包屑名稱直接填入。
先建立一個名為 routes.js 的檔案,統一將路由定義在這裡:
import { Home, Books, Electronics, Mobile, Desktop, Laptop } from './pages';
const routes = [
{
path: '/',
component: Home,
exact: true,
breadcrumbName: 'Home'
},
{
path: '/books',
component: Books,
breadcrumbName: 'Book'
},
{
path: '/electronics',
component: Electronics,
breadcrumbName: 'Electronics',
routes: [
{
path: '/electronics/mobile',
component: Mobile,
breadcrumbName: 'Mobile Phone'
},
{
path: '/electronics/desktop',
component: Desktop,
breadcrumbName: 'Desktop PC'
},
{
path: '/electronics/laptop',
component: Laptop,
breadcrumbName: 'Laptop'
}
]
}
];
export default routes;
接著在有使用 <Route /> 組件的地方,原本是寫死在裡面的,現在改成用這個路由設定來產生,例如原本的 App.js 中路由 <Route />的部分是這樣寫:
class App extends Component {
render() {
return (
<div className="container">
<Navbar />
<Route path="/" exact component={Home} />
<Route path="/books" component={Books} />
<Route path="/electronics" component={Electronics} />
</div>
);
}
}
export default App;
可以改成:
import routes from './routes';
class App extends Component {
render() {
return (
<div className="container">
<Navbar />
{}
{routes.map((route, i) => {
const { path, exact, routes } = route;
return (
<Route
key={i}
path={path}
exact={exact}
render={(routeProps) => (
<route.component routes={routes} {...routeProps} />
)}
/>
);
})}
</div>
);
}
}
export default App;
- 我們先把寫好的路由設定(route config)透過 import 載入進來。
- 接著把在 routes 設定檔中寫好的 path, exact 透過 props 傳進去 <Route path={path} exact={exact} /> 。
- 對於有使用到嵌套式路由的頁面,為了要把嵌套在內的 routes 傳到該頁面內,我們不能直接寫 <Route component={PageComponent} /> ,因為這種寫法無法把資料透過 props 傳到頁面內。因此需要使用 React-Router 中另外提供的 render 屬性。
- 在 render 屬性中需要代入一個函式,並回傳要渲染的頁面,例如, render={() => <PageComponent />} ,這種寫法可以把資料透過 props 傳到某一 Page 當中。
- 如果 routes 裡面還有 routes 表示它是嵌套式路由(nesting routes),一層路由裡還有其他路由,這時候要把它當成該頁面的組件傳進去,所以會有 render={() => <PageComponent routes={routes} />} 的寫法。
- 在 render 屬性後面接的這個函式中,可以接收一個參數,我們把這個參數取名為 routeProps ,routeProps 會傳回原本在 <Route /> 中可以拿到的 match, location, history 等屬性。接著透過 {...routeProps} 可以在把這些屬性注回到 Page 當中。寫起來會是這樣, render={(routeProps) => <PageComponent routes={routes} {...routeProps}/>} 。
- 最後, <route.component /> 可以動態指定要渲染的 Page 為何。
如果你覺得上面這樣的寫法太複雜了,你還無法理解,可以先跳過繼續往後閱讀沒關係。
同樣的,因為在 Electronics 頁面中也有使用到 <Route> 組件,因此也可以改成這樣的寫法:
const Electronics = ({ routes }) => {
return (
<div>
<h1 className="py-3">Electronics</h1>
<Switch>
{}
{routes.map((route, i) => {
const { path, exact, routes } = route;
return (
<Route
key={i}
path={path}
exact={exact}
render={(routeProps) => (
<route.component routes={routes} {...routeProps} />
)}
/>
);
})}
</Switch>
</div>
);
};
- 首先把在 App.js 時透過 React Router render={() => <PageComponent routes={routes} />} 傳進來的 routes 拿出來。
- 和上面使用一樣的方法,透過 {routes.map()} 去把所有相關的 <Route /> 組件組出來。
改成這樣之後,路由還是可以正常切換。
或許你會覺得在每個頁面中,都要透過 {routes.map()} 這一大塊程式碼,才能渲染原本的路由很麻煩,好在當我們定義集中式路由之後,React Router 提供了我們 react-router-config 這個套件,這裡面提供了 renderRoutes 這個方法可以幫我們省去寫一大段程式碼的麻煩,而它的原理和我們剛剛實作的方法是很類似的。
$ npm install react-router-config
安裝好之後就可以來整理一下上面的程式碼,首先是 App.js:
import React, { Component } from 'react';
import { renderRoutes } from 'react-router-config';
import { Navbar } from './components';
import routes from './routes';
class App extends Component {
render() {
return (
<div className="container">
<Navbar />
{}
{renderRoutes(routes)}
</div>
);
}
}
export default App;
- 記得先 import renderRoutes,再把 routes 帶進去,{renderRoutes(routes)},它就會幫你產出相對應的 <Router /> 組件。
在 Electronics 頁面中的寫法和剛剛類似,只有些微不同,不是直接拿 routes ,因為它把 routes 又包在 route 內,因此是拿 route.routes:
import React from 'react';
import { renderRoutes } from 'react-router-config';
import { Nav, ElectronicsNav } from './components';
const Electronics = ({ route }) => {
return (
<div>
<h1 className="py-3">Electronics</h1>
{renderRoutes(route.routes)}
</div>
);
};
一整個乾淨清爽的感覺,是不是覺得精簡非常多啊!你可能會想為什麼不早點把這好東西拿出來!?哎呀,了解一下背後的原理也是不錯的嘛。
如果畫面以及路由切換都和剛剛一樣正常運作的話,就表示程式碼應該沒什麼問題。
為了要取得每一個路由所對應到的麵包屑名稱,我們把路由的寫法改成路由設定檔(route config)的方式,接下來就要在特定的路由下取得麵包屑的名稱。
每個透過 <Route /> 組件產生的頁面,都會帶有透過 React Router 添加的屬性,可以透過該頁面的 props 屬性取得,其中包含 history, location, match 和 route。
舉例來說,在 Mobile 這個頁面中,可以把 props 透過 console.log 顯示出來看一下:
const Mobile = (props) => {
console.log('props in Mobile', props);
return <h3>Mobile Phone</h3>;
};
當在瀏覽器的導覽列輸入 localhost:3000/electronics/mobile 時,可以在 console 中看到 React Router 添加的屬性,其中 location.pathname 屬性可以讓我們知道當前瀏覽器網址列所在的路徑為何:
而 route 屬性則是來自當初設定好的路由配置,因此在這裡可以看到添加進去的 breadcrumbName 屬性。也就是說從該頁面的 props 就可以知道它的 breadcrumbName 為何:
從上面的例子可以看到,雖然直接根據頁面內的 route.breadcrumbName 屬性就知道該頁面的麵包屑名稱是什麼。但是「嵌套式路由」中除了需要知道當前路由的麵包屑名稱外,還需要知道它上一層的名稱。
例如,當網址當前的路由是 /electronics/mobile 時,雖然可以知道這個 Page 的名稱是 Mobile Phone,但同時還需要知道 /electronics 的麵包屑名稱是 Electronics,因為麵包屑組起來是這樣的:
Home > Electronics > Mobile Phone
這時候我們需要使用到 react-router-config 提供的另一個方法,稱作 matchRoutes。
matchRoutes 基本的使用方式像這樣,前面放當初定義好的路由設定檔,後面則放當前網址列的路由:
matchedRoutes = matchRoutes(routes, pathname);
把它寫到最到 Electronics 頁面中 console.log() 出來看看:
import { renderRoutes, matchRoutes } from 'react-router-config';
import routes from './routes';
const Electronics = ({ route, location }) => {
const matchedRoutes = matchRoutes(routes, location.pathname);
console.log('matchedRoutes in Electronics', matchedRoutes);
return (
<div>
<h1 className="py-3">Electronics</h1>
{renderRoutes(route.routes)}
</div>
);
};
當在瀏覽器導覽列輸入 localhost:3000/electronics/mobile 時,從 console 的結果可以看到,透過 matchRoutes 這個方法,除了可以拿到當前路由的 breadcrumbName 外,還可以把上一層路由的麵包屑名稱也拿到,也就是說,透過 matchRoutes 取得的資訊,就可以幫助我們組出麵包屑了:
簡單的把 Electronics 頁面改一下,就可以製作出我們想要的麵包屑了:
import { renderRoutes, matchRoutes } from 'react-router-config';
import { Link } from 'react-router-dom';
import routes from './routes';
const Electronics = ({ route, location }) => {
const matchedRoutes = matchRoutes(routes, location.pathname);
return (
<div>
<h1 className="py-3">Electronics</h1>
{}
<nav>
<ol className="breadcrumb">
{matchedRoutes.map((matchRoute, i) => {
const { path, breadcrumbName } = matchRoute.route;
return (
<li key={i} className="breadcrumb-item">
<Link to={path}>{breadcrumbName} </Link>
</li>
);
})}
</ol>
</nav>
{renderRoutes(route.routes)}
</div>
);
};
- 透過 matchRoutes() 可以取得所有從該網址 URL 開始,向上層推算的所有路由的麵包屑名稱
- 將配對出的 matchedRoutes 透過 map 來跑迴圈,疊代出每一個麵包名稱(breadcrumbName)和路由的路徑( path)。
現在,當你在 localhost:3000/electronics/ 路由內的頁面就都可以看到麵包屑了:
因為我們在 Home 和 Books 頁面都還沒放麵包屑進去,所以在那兩個頁面自然還不會看到麵包屑,再把麵包屑放入 Home 和 Books 頁面前,先再把這個麵包屑優化一下。
在剛剛的畫面中,你會發現即使已經在 Mobile 這一頁,Mobile Phone 的麵包屑仍然是可以點擊的連結,但一般來說當使用者已經在這個頁面時,該麵包屑就不該還可以被點擊:
這裡我們可以簡單判斷,先定一個名為 isActive 的變數,如果當前網址列的 URL 和 matchedRoutes 中 route 的 path 一樣時(location.pathname === route.path),表示這個配對到的路由就是使用者目前所在的頁面,isActive 會是 true,這時候就不要使用 <Link /> 產生連結。大概是這樣:
const Electronics = ({ route, location }) => {
<nav>
<ol className="breadcrumb">
{matchedRoutes.map((matchRoute, i) => {
const { path, breadcrumbName } = matchRoute.route;
const isActive = path === location.pathname;
return isActive ? (
<li key={i} className="breadcrumb-item active">
{breadcrumbName}
</li>
) : (
<li key={i} className="breadcrumb-item">
<Link to={path}>{breadcrumbName} </Link>
</li>
);
})}
</ol>
</nav>;
};
這時候使用者當前所在頁面的麵包屑就不會亮起,也不可點擊:
到目前為止麵包屑的功能已經差不多了,但還有一個可以優化的地方,像是這頁如果我們想在 Electronics 前面多一個 Home 的麵包屑,像是這樣 Home / Electronics / Mobile Phone 該怎麼辦呢?
方法不難,既然我們可以是透過 matchedRoutes 透過迴圈去跑出所有的麵包屑,只要我們改一下 matchedRoutes 這個陣列的內容,自然可以客製化出想要的麵包屑:
const Electronics = ({ route, location }) => {
let matchedRoutes = matchRoutes(routes, location.pathname);
matchedRoutes = [
{
route: {
path: '/',
breadcrumbName: 'Home'
}
},
...matchedRoutes
];
return (
);
};
在原本的 matchedRoues 陣列中,多添加了一個 route 物件,如此在稍後產生麵包屑的時候,自然就會多 Home 的麵包屑:
寫到這裡已經完成了麵包屑,最後因為在 Home, Books 頁面中也都會使用到麵包屑,因此可以把剛剛寫在 Electronics 頁面中的麵包屑拆成一個組件,直接套用到其他有需要使用的頁面即可:
const Electronics = ({ route, location }) => {
return (
<div>
<h1 className="py-3">Electronics</h1>
{}
<Breadcrumb locationPath={location.pathname} />
{renderRoutes(route.routes)}
</div>
);
};
把原本的內容放到 components.js 中:
import { matchRoutes } from 'react-router-config';
import routes from './routes';
const Breadcrumb = ({ locationPath }) => {
let matchedRoutes = matchRoutes(routes, locationPath);
return (
<nav>
<ol className="breadcrumb">
{matchedRoutes.map((matchRoute, i) => {
const { path, breadcrumbName } = matchRoute.route;
const isActive = path === locationPath;
return isActive ? (
<li key={i} className="breadcrumb-item active">
{breadcrumbName}
</li>
) : (
<li key={i} className="breadcrumb-item">
<Link to={path}>{breadcrumbName} </Link>
</li>
);
})}
</ol>
</nav>
);
};
export { Navbar, Breadcrumb };
這時候因為已經把 breadcrumb 抽成一個 component,所以剛剛透過修改 matchedRoutes 來客制化麵包屑的方式不能寫死在 <Breadcrumb /> 中,而是應該要可以在不同的頁面客製化出不同的麵包屑內容。
因此,我們在 <Breadcrumb /> 組件中新增一個名為 onMatchedRoutes 的 callback function:
const Breadcrumb = ({ locationPath, onMatchedRoutes }) => {
let matchedRoutes = matchRoutes(routes, locationPath);
if (typeof onMatchedRoutes === 'function') {
matchedRoutes = onMatchedRoutes(matchedRoutes);
}
return (
);
};
讓使用者在使用頁面中使用這個組件時,還有機會去修改要顯示的麵包屑為何:
const Electronics = ({ route, location }) => {
const onMatchedRoutes = (matchedRoutes) => {
return [
{
route: {
path: '/',
breadcrumbName: 'Home'
}
},
...matchedRoutes
];
};
return (
<div>
<h1 className="py-3">Electronics</h1>
{}
<Breadcrumb
locationPath={location.pathname}
onMatchedRoutes={onMatchedRoutes}
/>
{renderRoutes(route.routes)}
</div>
);
};
寫到這裡就大功告成拉!最後我們把 <Breadcrumb /> 也放到 Home 和 Books 中:
const Home = ({ location }) => {
return (
<div>
<h1 className="py-3">Home</h1>
<Breadcrumb locationPath={location.pathname} />
</div>
);
};
const Books = ({ location }) => {
const onMatchedRoutes = (matchedRoutes) => {
return [
{
route: {
path: '/',
breadcrumbName: 'Home'
}
},
...matchedRoutes
];
};
return (
<div>
<h1 className="py-3">Books</h1>
<Breadcrumb
locationPath={location.pathname}
onMatchedRoutes={onMatchedRoutes}
/>
</div>
);
};
完成後的畫面就像這樣子,麵包屑可以根據路由自動變換,如果有需要客製化添加麵包屑的地方,也可以透過 onMatchedRoutes 這個 callback function 來修改: