iT邦幫忙

2024 iThome 鐵人賽

DAY 11
0

在之前的學習中,我們探討了如何優化應用的性能和結構。隨著應用的增大,將所有功能集中在一個組件裡變得不夠靈活,因此,我們將進一步模組化應用功能,讓結構更加清晰,便於維護和擴展。

今天的目標是將功能分離到各自的頁面,並使用 React Router 構建靈活的頁面導航系統,這為我們後續的作品集網站開發打下基礎。我們還將新增一個 Home Page,讓用戶可以選擇進入不同的應用階段。

React Router 與頁面導航

React Router 是 React 中的標準路由解決方案,能根據 URL 渲染對應的組件,實現單頁應用(SPA)的多頁效果。它能夠讓應用在不同頁面之間切換,而不必重載整個應用。

通過將邏輯分離到不同頁面,我們可以提升復用性和可維護性。例如,導航欄和頁腳可以統一管理,不需要在每個頁面中重複實現。嵌套路由允許我們在頂層路由中定義共用的佈局(如 Layout 組件),並通過 Outlet 組件渲染子路由的內容,確保共用組件(如導航欄和頁腳)在頁面切換時保持不變。。

設計目標

今天的主要目標是進一步模組化應用並實現靈活的頁面導航。具體步驟如下:

  1. 新增 Home Page :讓用戶可以在首頁選擇進入基礎階段(BasicStage)或進階階段(IntermediateStage),提升應用的導引性和用戶體驗。
  2. 模組化功能並分離頁面邏輯:將原本集中在 App.jsx 中的邏輯與功能拆分到專屬的頁面模組中(如 BasicStageLayout),使應用結構更加清晰,便於維護和擴展。
  3. 設置路由及嵌套路由:實現共用組件(如導航欄、頁腳)的重用,確保應用結構統一且易於擴展,同時處理無效路徑時顯示通用的 404 錯誤頁面,提升整體應用的穩定性。

以下是修改後的專案目錄結構,展示了今天所新增或修改的部分:

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 應用

實際演練

Step 1: 設置 React Router

首先,我們需要開啟終端機,並輸入以下指令來安裝 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>
);

Step 2: 新增 Home Page

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 組件不僅負責展示簡單的內容,還提供了進入應用不同部分的導航入口,讓用戶能夠快速選擇進入不同的階段。

Step 3: 設置 Layout 組件與嵌套路由

Layout 是應用的主框架,負責呈現應用的全局佈局,包括導航欄、頁腳以及其他全局功能。我們將原本位於 App.jsx 中的將 ThemeButtonLangButton 元件移動至 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

說明

  • Header:包含了主題與語言切換按鈕,確保這些全局功能無論頁面如何變換都能被使用。
  • Footer:用來顯示應用的版本資訊等全局性資料。
  • Outlet:由 React Router 提供,用來動態渲染子路由對應的頁面內容。這樣,頁面的主要結構(如導航、頁腳)能夠保持不變,只需更新主體內容。

Step 4: 路由配置

最後,我們需要在 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;

說明

  • RoutesRoute:定義了應用的導航結構。Layout 作為主佈局,HomePage 作為首頁,而 BasicStageIntermediateStage 則作為應用的其他頁面。
  • path="*":捕捉所有未定義的路徑,展示 404 頁面,這是應用處理錯誤路徑的關鍵步驟。

結語

今天,我們成功將應用功能模組化,並通過引入 React Router 建立靈活的頁面導航系統。同時,我們新增了 Home Page,提升了用戶體驗。我們還使用嵌套路由實現共用組件的持續顯示,並處理 404 錯誤頁面,進一步提升應用的靈活性與穩定性。

Day11

你是否曾經遇到過頁面導航混亂、無法正確處理錯誤路徑的問題?通過使用 React Router,你可以建立一個結構清晰且高度可維護的應用。

接下來的學習中,我們將進入 Intermediate Stage,並開始作品集網站的完整開發,探索頁面佈局、動畫效果等更高階的主題,最終實現一個現代、響應式的個人作品集網站。

此外,我們已將完整的代碼實作與更多練習題上傳至 GitHub,鼓勵大家前往查看,並回顧文章中的概念,挑戰更進階的優化練習。
👉 前往 GitHub 的 v0.11.0-react-router-navigation 查看完整程式碼


流光館Luma<∕> ✨ 期待與你繼續探索更多技術知識!



上一篇
Day 10 : Lazy Loading 優化 React 大文件加載
下一篇
Day 12: 打造 React 導航欄元件
系列文
從PM到前端開發:我的React作品集之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言