[ReactRouter] Bread Crumb
這篇文章主要說明如何整合 react-router 來製作一個具有導覽功能的麵包屑(breadcrumb),也就是這個麵包屑可以根據當前使用者瀏覽的路由動態顯示出對應的名稱。由於會說明之所以這麼做的思路,因此篇幅較長;如果想要直接看如何使用這段程式碼,可以到 Github 上檢視 ReadMe,當中的說明較為精簡。
另外,不會從頭開始說明 React 和 React Router 的使用,因此建議閱讀前應 該具備基本的 React 和 React Router 知識,不然可能會看得相當吃力。
來看看怎麼做吧!
建立 React 專案
在這裡我們直接使用 create-react-app
來建立一個簡單的 React 專案,就稱作 react-router-breadcrumb
:
$ create-react-app react-router-breadcrumb # 透過 create-react-app 建立專案
$ cd react-router-breadcrumb # 進入建立好的專案資料夾
另外,需要使用到 react-router-dom
來幫我們建立路由:
$ npm install react-router-dom # 安裝 react-router-dom
接著就可以啟動專案,然後到 localhost:3000
即可看到預設的畫面:
$ npm run start # 啟動專案
在這篇文章中不會說明
create-react-app
的使用,若有需要可自參閱到create-react-app
的官方文件。
前置清理與載入樣式
為了讓我們的畫面比較乾淨一些,就先直接套 Bootstrap 4 進來用,如果不想套的話也是可以,就是畫面會比較呆板一些。
在 /public/index.html
中把 Bootstrap 4 的 CDN 連結套用進來:
<!-- /public/index.html -->
<!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" />
<!-- import bootstrap here -->
<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>
<!-- libs below are for bootstrap -->
<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」確認沒問題就好:
// /src/App.js
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 了:
如果到這一步有問題的話,可以對照看看這個 commit。
建立需要的頁面 pages 和 components
清理完後就可以來建立所需要的頁面。
建立頁面(Pages)
先把在這個專案中會使用的頁面建立起來,可以想像一個商城的結構大概是這樣,我們有三個外層的路由,分別是「首頁」、「書籍館」和「3C 商品館」,而 「3C 商品館」中又會細分出「手機館」、「桌機館」和「筆電館」,從這樣的結構可以看出,將會使用到嵌套式路由(Nested Routing):
- Home # 首頁
- Books # 書籍館
- Electronics # 3C 商品館
--- Mobile # 手機館
--- Desktop # 桌機館
--- Laptop # 筆電館
因為頁面(Page)的內容不是我們的重點,所以在本文中把所有的頁面元件(Page component)都寫在一支
pages.js
的檔案中。
// /src/pages.js
import React from 'react';
/**
* These are root pages
*/
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>;
};
/**
* These are pages nested in Electronics
*/
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 };
對照目前的 commit。
建立基本的路由
再來我們先建立基本的路由,以便透過輸入網址連到這些頁面。
在 index.js
中,先載入 BrowserRouter
和 Switch
:
// /src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Switch } from 'react-router-dom';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Switch>
<App />
</Switch>
</BrowserRouter>
);
在 App.js
中,把當初 create-react-app
建立但用不到的內容都砍掉,只需要指定不同的路由應該要對應到哪些頁面就好,對於 <Route />
這個元件的概念不用想得太複雜,簡單理解成就是當瀏覽器網址列的 URL 和這個 path
相匹配到時,就會在「這個位置」顯示該 component
:
// /src/App.js
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">
{/* The corresponding component will show here if the current URL matches the path */}
<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
元件中,加上路由:
// /src/page.js
import { Switch, Route } from 'react-router-dom';
// ...
const Electronics = () => {
return (
<div>
<h1>Electronics</h1>
<Switch>
{/* The component will show here if the current URL matches the path */}
<Route path="/electronics/mobile" component={Mobile} />
<Route path="/electronics/desktop" component={Desktop} />
<Route path="/electronics/laptop" component={Laptop} />
</Switch>
</div>
);
};
// ...
這時候當我們在輸入網址列 /electronics/mobile
時,也會出現相對應的畫面:
- 關於路由的配置可進一步參考 React Router 官方文件。
- 如果撰寫過程中有問題,可以和此 commit 對照。
建 立導覽列元件
每一次都要從網址列輸入網址實在有點麻煩,既然路由都配置好了,先來做個導覽列方便使用吧。
為了方便示範,而且這個專佔中不會有太多的 React 元件,我們把在頁面中會套用到的元件都放在一隻叫做 components.js
的檔案中。
Navbar
基本上就是直接套用 Bootstrap 4 Navbar 的結構和樣式,並且搭配 react-router-dom
的 <Link>
來建立連結:
// /src/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 />
即可:
// /src/App.js
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">
{/* Put Navbar Here */}
<Navbar />
<Route path="/" exact component={Home} />
<Route path="/books" component={Books} />
<Route path="/electronics" component={Electronics} />
</div>
);
}
}
export default App;
到目前為止完成畫面差不多完成了:
如果撰寫過程中有問題,可以和此 commit 對照。