iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0
JavaScript

從PM到前端開發:我的React作品集之旅系列 第 6

Day 6:基礎模組化設計優化 React 應用

  • 分享至 

  • xImage
  •  

在上一篇文章中,我們介紹了如何通過 SASS 和 react-i18next 結合,實現 React 應用的主題和語言切換功能,讓用戶可以自由切換語言和主題風格。然而,隨著功能的不斷增多,我們發現 App.js 的邏輯變得愈發複雜且難以維護。這給我們的開發和擴展帶來了極大的挑戰。

因此,本篇文章將介紹如何運用基礎模組化設計,將主題切換和語言切換功能拆分為獨立的元件,解決按鈕無法跟隨主題變更的問題,從而改善應用的維護性和擴展性。這是一個循序漸進的過程,適合正在學習模組化設計的開發者進行練習。此外,我們會將 Logo 的元件實作作為讀者練習的一部分,鼓勵大家自行設計和實現。

Day06

為什麼需要基礎模組化設計

基礎模組化設計是將應用邏輯拆分成獨立的組件(components),使每個組件專注於自己的功能。這樣的設計使應用更加清晰、易於維護和擴展,尤其適合管理日益複雜的應用邏輯。

隨著應用邏輯的增長,像 App.js 這樣的核心元件會逐漸承擔過多的責任,包括狀態管理和各種功能邏輯。這會導致以下問題:

  • 可讀性差:代碼變得冗長且難以理解。
  • 難以維護:修改一個功能容易牽連其他邏輯。
  • 擴展困難:加入新功能變得複雜,因為要處理多個混合在一起的邏輯。

因此,我們將把應用中的主要功能(例如主題切換和語言切換)從 App.js 中拆分出來,並封裝成獨立的組件。這不僅能讓程式碼結構更清晰,還有助於未來的功能擴展。基礎模組化設計的主要目標是通過職責分離,減少耦合,讓每個組件更加專注和易於管理。

設計目標

在本篇文章中,我們將進一步拆分主題切換(ThemeButton)、語言切換(LangButton)。通過這種模組化設計,應用變得更加靈活,且日後的擴展與維護成本也會大幅降低。以下是預計本次修改或新增的檔案,並展示這些變動如何提升應用的模組化設計。如果對專案結構不熟悉,建議先參考 [Day 1:用 Webpack 初始化 Day 1:用 Webpack 初始化 React 專案

react-webpack-starter/
├── src/                    # React 原始碼
│   ├── components/         # React 組件
│   │   ├── LangButton.jsx  # 語言切換按鈕
│   │   ├── LangButton.module.scss  # 語言切換按鈕樣式
│   │   ├── Logo.jsx        # 應用 Logo
│   │   ├── Logo.module.scss # Logo 樣式
│   │   ├── ThemeButton.jsx # 主題切換按鈕
│   │   └── ThemeButton.module.scss # 主題切換按鈕樣式
│   ├── styles/             # 全域樣式
│   │   ├── _button.scss    # 按鈕樣式
│   │   └── _theme.scss     # 主題樣式
│   ├── utils/              # 工具函數和輔助功能
│   │   └── i18n.js         # 語言配置文件
│   ├── App.js              # 主應用組件
│   ├── APP.module.scss     # 主應用樣式模組

通過這種模組化設計,邏輯被劃分到各自的元件,大大減輕了 App.js 的負擔,使應用結構更加清晰且易於維護。接下來,我們將詳細討論各元件的實作細節。

實際演練:模組化設計

Step 1: 定義共用的 Sass 樣式

首先,我們會在 src/styles/ 資料夾下創建一個 _button.scss 檔案,統一管理按鈕的共用樣式,使得樣式能夠在不同元件中共用。

// _button.scss
@mixin button-styles($bg-color, $text-color) {
    padding: 10px 20px;
    font-size: 16px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s;
    background-color: $bg-color;
    color: $text-color;

    &:hover {
        background-color: darken($bg-color, 10%);
    }
}

這個 mixin 可以靈活地應用在任何按鈕中,確保樣式一致。通過傳入兩個的顏色參數$bg-color$text-color,可以定義不同主題的按鈕樣式。

Step 2: 建立主題切換按鈕 ThemeButton.jsx

接著,在 src/components/ 目錄中,建立一個新的元件 ThemeButton.jsx,負責切換應用的主題。

import React from 'react';
import * as styles from './ThemeButton.module.scss';

const ThemeButton = ({ isDarkMode, toggleMode }) => {
    return (
        <button
            className={isDarkMode ? styles.dark_theme_btn : styles.light_theme_btn}
            onClick={toggleMode}>
            切換到 {isDarkMode ? 'Light Mode' : 'Dark Mode'}
        </button>
    );
}
export default ThemeButton;

接下來,我們為這個按鈕建立樣式文件 ThemeButton.module.scss,並引入按鈕共用樣式 _button.scss_theme.scss,確保按鈕的樣式能跟著主題進行切換。

@import '@/styles/button';
@import '@/styles/theme';

.light_theme_btn {
    margin: 10px;
    @include button-styles($button-primary-background, $button-primary-text)
}

.dark_theme_btn {
    margin: 10px;
    @include button-styles($button-dark-primary-background, $button-dark-primary-text);
}

Step 3: 建立 LangButton.jsx

語言切換按鈕與主題切換的邏輯類似,但這次我們要處理的是應用的語言環境切換。

import React from 'react'
import i18n from '@/utils/i18n';
import * as styles from './LangButton.module.scss';

const LangButton = ({ isDarkMode }) => {
    // 切換語言選項的函數
    const toggleLanguage = () => {
        const newLang = i18n.language === 'en' ? 'zh' : 'en';
        i18n.changeLanguage(newLang);
    }

    return (
        <button
            className={isDarkMode ? styles.dark_lang_btn : styles.light_lang_btn}
            onClick={toggleLanguage}>
            🌐 {i18n.language.toUpperCase()} ▼
        </button>
    )
}

export default LangButton

接著,為語言切換按鈕建立樣式文件 LangButton.module.scss,並使用之前定義的共用樣式:

@import '@/styles/button';
@import '@/styles/theme';

.light_lang_btn {
    margin: 10px;
    @include button-styles($button-primary-background, $button-primary-text)
}

.dark_lang_btn {
    margin: 10px;
    @include button-styles($button-dark-primary-background, $button-dark-primary-text);
}

Step 4: 自行實作 Logo 組件

在本次練習中,我們已經展示了如何模組化主題切換按鈕和語言切換按鈕的功能。現在,作為進一步的練習,我們將 Logo 的實作留給讀者自行完成。你可以嘗試創建一個 Logo 元件,並將其樣式與應用分離,實現模組化設計。

這將是一個很好的機會來應用你學到的知識,並進一步加深你對模組化設計的理解。可以參考前面按鈕的步驟,為 Logo 元件設計自己的樣式和邏輯

Step 5: 更新 App.js 文件

最後,在 App.js 中使用別名方式引入 ThemeButton、LangButton 和你自行實作的 Logo。這樣可以進一步減少 App.js 的程式碼複雜度。

import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import i18n from '@/utils/i18n';
import ThemeButton from '@/components/ThemeButton';
import LangButton from '@/components/LangButton';
import Logo from '@/components/Logo';
import * as styles from "@/APP.module.scss";
import '@/styles/_language.scss';

const App = () => {

    // 使用 useTranslation 取得翻譯函數 t
    const { t } = useTranslation();
    const [isDarkMode, setIsDarkMode] = useState(false);

    const toggleMode = () => {
        setIsDarkMode(!isDarkMode);
    };

    return (
        <div className={`${styles.appContainer}
            ${isDarkMode ? styles.darkMode : styles.lightMode}
            ${i18n.language}
            `}>
            <Logo isDarkMode={isDarkMode} />
            <h1>{t('welcome')}</h1>
            <p>{t('description')}</p>
            <div>
                <ThemeButton isDarkMode={isDarkMode} toggleMode={toggleMode} />
                <LangButton isDarkMode={isDarkMode} />
            </div>
        </div >
    );
};

export default App;

這樣,原本 46 行的程式碼已經縮減到 34 行,結構更為清晰,且更易於維護。

結語

在這篇文章中,我們展示了如何將 React 應用中的主題切換和語言切換功能進行模組化設計。通過將這些邏輯拆分成獨立的元件並使用 SASS Mixin 共用樣式,我們成功減輕了 App.js 的負擔,使代碼結構更加清晰、易於維護和擴展。

你現在已經掌握了模組化設計的基礎,這讓你的應用代碼變得更清晰、易於管理。但隨著應用規模的擴大,僅僅依靠基礎的模組化設計可能不足以應對更複雜的功能需求。比如,當你的應用需要頻繁地在不同元件間共享狀態時,將邏輯封裝到 Context 這樣的進階工具中,能幫助你更高效地管理狀態,保持代碼的整潔和靈活性。

在下一篇文章中,我們將討論如何將主題切換邏輯封裝到類似 ThemeProviderContext 中,使得主題切換功能變得更加集中和靈活。這將進一步優化代碼結構,並提供一個更高效的方式來管理應用的主題狀態。

你是否感覺模組化設計讓應用的邏輯更加清晰了?接下來,我們將通過使用 ThemeProvider 模式來處理主題切換,進一步提升應用的可維護性和擴展性。

此外,部分修正內容尚未完全在文章中展示,完整的代碼實作及進一步的練習題已上傳至 GitHub。歡迎大家前往查看,並嘗試自行完成 Logo 組件的模組化設計。

👉 前往 GitHub 的 v0.6.0-modularized_components查看完整代碼


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



上一篇
Day 5:整合 i18n,打造多語言 React 應用
下一篇
Day 7:進階模組化設計之 ThemeProvider 應用
系列文
從PM到前端開發:我的React作品集之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言