--- 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` 要分清楚,搞錯就報錯!