---
tags: React.js
---
# React Router
## 建立環境
1. 先用 create-react-app 去寫架構
2. 架好環境後再安裝 React router
請準備 node v14 以上的版本
在桌面新增一個資料夾 react-test
開啟終端機 移入到該資料夾
執行 `npx create-react-app myproject`
就會在 react-test 資料夾裡面建立一個 myproject 資料夾了
接下來可以 通過終端機 cd 到 myproject 資料夾
執行 `npm start` 就會運行環境並自動開啟瀏覽器
如果瀏覽器中有看到 React logo 在轉圈圈,即表示環境建立成功!
## 安裝 Router
環境建好後 來安裝 router 套件
一樣開啟終端機,位置在 myproject 資料夾裡面
執行 `npm install react-router-dom@6`
接下來按照[官方說明文件](https://reactrouter.com/docs/en/v6/getting-started/installation#create-react-app)嘗試使用 router(從 Create React App 那邊開始看)
按照行數標記+符號的地方,整行複製內容到 myproject 對應的檔案位置貼上
### step1
src/index.js 完成結果:
```jsx=
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom"; // +
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
// +
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
```
### step2
src/App.js 完成結果:
```jsx=
import logo from './logo.svg';
import { Routes, Route, Link } from "react-router-dom"; // +
import './App.css';
// +
function App() {
return (
<div className="App">
<h1>Welcome to React Router!</h1>
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
</div>
);
}
// + Home() & About()
function Home() {
return (
<>
<main>
<h2>Welcome to the homepage!</h2>
<p>You can do this, I believe in you.</p>
</main>
<nav>
<Link to="/about">About</Link>
</nav>
</>
);
}
function About() {
return (
<>
<main>
<h2>Who are we?</h2>
<p>
That feels like an existential question, don't you
think?
</p>
</main>
<nav>
<Link to="/">Home</Link>
</nav>
</>
);
}
export default App;
```
### step3
看一下瀏覽器開始的畫面是否有出現 Home、About 連結
且點擊後可正確切換內容元件
當需要頁面切換但又不希望頁面閃爍的話
就可以用 router 的 Link 取代 a 連結
像這樣:
```jsx=
<nav>
<Link to="/">Home</Link>
</nav>
```
再通過 router 設定路由表(path 對應哪個 element)
像這樣:
```jsx=
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
```
就可以根據 path&element 替換元件
如果是不存在的路由,默認就是不顯示任何元件
如希望顯示404頁面樣式
則可以設定成:
```jsx=
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="categories" element={<Categories />} />
<Route path="faq" element={<Faq />} />
<Route path="contact" element={<ContactUs />} />
<Route path="*" element={<ErrorPage />} /> {/* path="*" 表示沒有符合的路由,就直接顯示404元件頁面 */}
</Routes>
```
### 路由設置說明
- `<Route ... />` 用來建立單個路由
- attr 中的 path 用來設定網址
- attr 中的 element 則用來設定要顯示的元件
> (React 裡面定義名稱首字母大寫的 function 都是元件)
那上面這種根據說明文件測試路由的方式,因為只是範例
所以把所有東西都塞在 src/App.js 檔案裡面
但如果路由很多、每個元件內容也都很長的話這隻檔案會很沒完沒了
此時就可以利用 import 方式,把元件獨立拉出一個資料夾做引用
### import 元件教學
我們在 src 資料夾中,新增一的 components 資料夾
在裡面在新增一個一個的 js 檔案去放元件
> 元件通常都是大坨蜂命名法
> 比如 About.js 、 Products.js 之類的檔案名稱設計
這邊的 import/export 牽涉到作用域,每支 .js 的元件檔案都是獨立的作用域
所以有在檔案中使用到的套件或語法,都需要在檔案中額外 import 才可以正確使用
不能因為最後使用的 src/App.js 裡面有 import 就不獨立再次載入
比如我把 About 元件單獨弄成一支 About.js 檔案,再從 src/App.js 引入
About.js 本身使用到的 Link 就也需要在檔案最開始加上:
```javascript=
import { Link } from "react-router-dom";
```
才可以正確使用喔!
## 部署到 github page
這邊說明一下網址格式的部分
因 github page 的網址如果是 / 表示要找資料夾
所以在部署之前要先到 src/index.js 裡面
把所有 BrowserRouter 替換成 HashRouter (讓網址變成`/#/`)
### 安裝 github page
在 myproject 裡面執行 `npm install --save gh-pages`
接著到自己的 github 中建立 repository 命名為 reactRouter
接下來執行 `git remote add origin https://github.com/{你的 github 帳號名稱}/reactRouter.git`
再來回到 myproject 找到 package.json
在檔案的最後面新增:
```json=
"homepage": "https://{你的 github 帳號名稱}.github.io/reactRouter"
```
再從該檔案往上找到 `"scripts": {...}` 新增下面那兩行:
```json=
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
```
最終會變成:
```json=
{
...,
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"predeploy": "npm run build", // +
"deploy": "gh-pages -d build" // +
},
...,
"homepage": "https://{你的 github 帳號名稱}.github.io/reactRouter" // +
}
```
最後回到終端機,在 myproject 執行 `npm run deploy`
跑完後開啟瀏覽器輸入 package.json 剛剛添加的 homepage 那個網址,就會看到上傳成功了~
## useNavigate(連結) 與 Link(連結) 轉址差異
在某些時候,因操作錯誤,要強制跳轉到某個頁面
就需要通過 JS 語法來實現網址跳轉
useNavigate 就是用於通過 JS 實現的連結
EX: 購物車結帳需是登入狀態,
當判斷後發現你不是登入裝態,需要通過 JS 把你跳轉到登入頁
就會使用 useNavigate 的方法實現
而 Link 則是類似 a 連結這樣的純粹 DOM 語法
在 React 中寫出 Link 都會被 JSX 編譯成普通 a 連結
## React Router 中的 Layout
正常多頁式網站都會有共用區域
比如 header、footer 這種重複性質的區塊
我們就可以把他們整成一個 Layout 元件
並在裡面需要替換的地方 指定成 Outlet 的元件
比如 About 與 FAQ 頁面都是共用的 header、footer,只有內容的 section 不同
我們就可以這樣做:
```jsx=
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route path="about" element={<About />} />
<Route path="faq" element={<Faq />} />
</Route>
</Routes>
);
}
function Layout() {
return (
<div>
<div className="header">
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
</div>
<Outlet />
<div className="footer">I'm FOOTER!</div>
</div>
);
}
```
`<Outlet />` 就是要替換的地方,到時候根據網址會自動顯示 faq 或 about 的元件內容
## 雙層路由的作法
```jsx=
<Routes> // 建立路由表,最外層要加 s
<Route path="/" element={<Layout />}> // 建立 layout 把所有路由包起來
<Route index element={<Home />} /> // index 屬性用來設置預設首頁內容
<Route path="about" element={<About />} />
<Route path="tour" element={<Tour />}> // 雙層路由,重點就是用路由標籤裡面再包路由標籤
<Route index element={<TourList />} />
<Route path=":id" element={<TourDetail />} /> // 動態路由
</Route>
<Route path="*" element={<ErrorPage />} /> // 404 頁面
</Route>
</Routes>
```
## 動態路由的做法
動態路由抓取 `path` 的 `:id` 值作法,
第一步:
`import { useParams } from "react-router-dom";`
第二步:
`const { Id } = useParams();`
> 難點:`import from react` 或是 `from react-router-dom` 要分清楚,搞錯就報錯!