在之前的學習中,我們探討了如何優化應用的性能和結構。隨著應用的增大,將所有功能集中在一個組件裡變得不夠靈活,因此,我們將進一步模組化應用功能,讓結構更加清晰,便於維護和擴展。
今天的目標是將功能分離到各自的頁面,並使用 React Router 構建靈活的頁面導航系統,這為我們後續的作品集網站開發打下基礎。我們還將新增一個 Home Page,讓用戶可以選擇進入不同的應用階段。
React Router 是 React 中的標準路由解決方案,能根據 URL 渲染對應的組件,實現單頁應用(SPA)的多頁效果。它能夠讓應用在不同頁面之間切換,而不必重載整個應用。
通過將邏輯分離到不同頁面,我們可以提升復用性和可維護性。例如,導航欄和頁腳可以統一管理,不需要在每個頁面中重複實現。嵌套路由允許我們在頂層路由中定義共用的佈局(如 Layout 組件),並通過 Outlet 組件渲染子路由的內容,確保共用組件(如導航欄和頁腳)在頁面切換時保持不變。。
今天的主要目標是進一步模組化應用並實現靈活的頁面導航。具體步驟如下:
App.jsx
中的邏輯與功能拆分到專屬的頁面模組中(如 BasicStage
和 Layout
),使應用結構更加清晰,便於維護和擴展。以下是修改後的專案目錄結構,展示了今天所新增或修改的部分:
react-webpack-starter/
├── src/ # React 原始碼
│ ├── components/ # React 組件
│ │ ├── Layout.module.scss # 布局樣式
│ │ └── Layout.jsx # 布局組件
│ ├── pages/ # 應用頁面
│ │ ├── BasicStage.jsx # 基礎階段頁面
│ │ ├── IntermediateStage.jsx # 進階階段頁面
│ │ └── HomePage.jsx # 應用入口頁面
│ ├── App.js # 主應用組件
│ └── APP.module.scss # 主應用樣式模組
這樣的目錄結構強調了應用的模組化設計,讓每個頁面和功能都有清晰的責任範疇,這有助於未來的維護和擴展。如果對模組化設計不熟悉,建議回顧 Day 6:基礎模組化設計優化 React 應用。
首先,我們需要開啟終端機,並輸入以下指令來安裝 react-router-dom,以啟用 React 的路由功能:
npm install react-router-dom
安裝完成後,我們需要將 BrowserRouter
引入應用中,包裹住我們的APP元件,這樣才能啟用路由功能。
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import '@/utils/i18n'; // 确保 i18n 初始化在应用渲染之前
import App from '@/App';
import { ThemeProvider } from '@/utils/ThemeContext';
import '@/styles/_global.scss' // 引入全局樣式
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<ThemeProvider>
<App />
</ThemeProvider>
</BrowserRouter>
);
HomePage
是用戶進入應用的第一個頁面,將為他們提供導航選項,讓他們選擇進入不同的階段頁面(基礎階段或進階階段)。
我們通過 Link
組件實現頁面導航,這是一種比傳統 <a>
標籤更高效的方法,因為它不會重新加載整個應用,只會更改 URL 和更新視圖。
// src/pages/HomePage.js
import React from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { log, logLevel } from '@/utils/log';
import { useTheme } from '@/utils/ThemeContext';
import * as styles from '@/pages/HomePage.module.scss'; // 使用模塊化的樣式
const HomePage = () => {
// 使用 useTranslation 取得翻譯函數 t
const { t, i18n } = useTranslation();
const { isDarkMode } = useTheme();
log(logLevel.DEBUG, 'HomePage rendered');
const backgroundImage = isDarkMode ? require('@/assets/bg/night_image.png') : require('@/assets/bg/day_image.png');
return (
<div className={`${styles.homeContainer} ${i18n.language}`}
style={{ backgroundImage: `url(${backgroundImage})` }}>
<div className={styles.content}>
<h1 className={styles.title}>{t('homepage.title')}</h1>
<p className={styles.subtitle}>{t('homepage.subtitle')}</p>
<div className={styles.buttonContainer}>
<Link to="/intro" className={
`${isDarkMode ? styles.dark_basic_btn : styles.light_basic_btn}`}>
{t('homepage.basic_stage')}
</Link>
<Link to="/portfolio" className={
`${isDarkMode ? styles.dark_adv_btn : styles.light_adv_btn}`
}>
{t('homepage.advanced_stage')}
</Link>
</div>
</div>
</div >
)
};
export default HomePage
HomePage
組件不僅負責展示簡單的內容,還提供了進入應用不同部分的導航入口,讓用戶能夠快速選擇進入不同的階段。
Layout
是應用的主框架,負責呈現應用的全局佈局,包括導航欄、頁腳以及其他全局功能。我們將原本位於 App.jsx
中的將 ThemeButton
和 LangButton
元件移動至 Layout
中,能夠確保無論使用者瀏覽到哪個頁面,這些按鈕始終保持在畫面中,提供一致的體驗。
以下是修改後的 Layout.jsx
:
//src/components/Layout.jsx
import React, { useMemo } from 'react'
import { Outlet } from 'react-router-dom';
import ThemeButton from '@/components/Buttons/ThemeButton';
import LangButton from '@/components/Buttons/LangButton';
import { useTheme } from '@/utils/ThemeContext'
import Version from '@/components/Version';
import * as styles from '@/components/Layout.module.scss'
import { useTranslation } from 'react-i18next';
const Layout = () => {
// 使用 useTranslation 取得翻譯函數 t
const { t } = useTranslation();
const { isDarkMode } = useTheme();
// 使用 useMemo 緩存翻譯文本
const buttonText = useMemo(() => {
return isDarkMode ? t('dark_mode') : t('light_mode');
}, [t, isDarkMode]);
return (
<div className={`${styles.appContainer}
${isDarkMode ? styles.darkMode : styles.lightMode}`}>
<header>
<nav>
<ThemeButton buttonText={buttonText} />
<LangButton />
</nav>
</header>
<main className={styles.mainContent}>
<Outlet /> {/* 這裡渲染頁面的主要內容 */}
</main>
<footer>
<Version />
</footer>
</div>
)
}
export default Layout
說明:
最後,我們需要在 App.jsx
中設置路由和嵌套路由:
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import HomePage from '@/pages/HomePage';
import BasicStage from '@/pages/BasicStage';
import Layout from '@/components/Layout';
import IntermediateStage from '@/pages/IntermediateStage';
const App = () => {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<HomePage />} /> {/* 默認首頁路由 */}
<Route path="intro" element={<BasicStage />} />
<Route path='portfolio' element={<IntermediateStage />} />
<Route path="*" element={<div>404 - Page Not Found</div>} /> {/* 通用 404 頁面 */}
</Route>
</Routes>
);
};
export default App;
說明:
Routes
和 Route
:定義了應用的導航結構。Layout
作為主佈局,HomePage
作為首頁,而 BasicStage
和 IntermediateStage
則作為應用的其他頁面。path="*"
:捕捉所有未定義的路徑,展示 404 頁面,這是應用處理錯誤路徑的關鍵步驟。今天,我們成功將應用功能模組化,並通過引入 React Router 建立靈活的頁面導航系統。同時,我們新增了 Home Page,提升了用戶體驗。我們還使用嵌套路由實現共用組件的持續顯示,並處理 404 錯誤頁面,進一步提升應用的靈活性與穩定性。
你是否曾經遇到過頁面導航混亂、無法正確處理錯誤路徑的問題?通過使用 React Router,你可以建立一個結構清晰且高度可維護的應用。
接下來的學習中,我們將進入 Intermediate Stage,並開始作品集網站的完整開發,探索頁面佈局、動畫效果等更高階的主題,最終實現一個現代、響應式的個人作品集網站。
此外,我們已將完整的代碼實作與更多練習題上傳至 GitHub,鼓勵大家前往查看,並回顧文章中的概念,挑戰更進階的優化練習。
👉 前往 GitHub 的 v0.11.0-react-router-navigation 查看完整程式碼
✨ 流光館Luma<∕> ✨ 期待與你繼續探索更多技術知識!