在過去的幾篇文章中,我們探討了 Hero Section 的設計和優化,包括使用 SVG 和動態控制來增強頁面交互效果。今天,我們將進一步完成個人網站的服務展示部分,深入討論如何動態生成一個 Card 元件,並通過 React 結合動態數據來實現卡片的展示。這篇文章將專注於如何通過動態數據驅動來靈活展示卡片內容,確保網站在不同裝置上具備高效的響應式設計。
Card 元件 是展示服務或產品的關鍵介面元素,尤其在響應式網頁中常見。為了提升靈活性和可擴展性,我們採用了 動態數據驅動 的設計方法,使得卡片內容能根據需求動態更新和調整。以下是我們的 Card 元件設計的核心原則:

我們的 Card 元件將具備以下屬性,這些屬性來自動態數據,並可以靈活地控制每張卡片的內容:
| 屬性 | 說明 | 類型 | 預設值 | 
|---|---|---|---|
| icon | 卡片頂部的圖標 | string | '' | 
| title | 卡片的標題 | string | '' | 
| description | 卡片的描述 | string | '' | 
| category | 卡片底部的類別 | string | '' | 
| details | 卡片的詳細資訊(技術或工具) | string | '' | 
| borderSVG | 自定義邊框(可選的 SVG 邊框) | node | null | 
這些屬性讓我們的 Card 元件 可以靈活展示不同服務的內容,包括服務名稱、描述、技術細節
i18n 配置首先,我們將卡片數據放入 i18n 的 resources 中,這樣我們就可以根據語言環境來動態獲取對應的卡片內容。
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import config from '@/utils/config';
import { logLevel } from '@/utils/log';
i18n.use(initReactI18next).init({
    resources: {
        en: {
            translation: {
                // .. 其他翻譯
               cardsData: [
                    {
                        icon: "React.png",
                        title: "Frontend Developer",
                        description: "Building engaging, responsive websites that transform ideas into seamless user experiences.",
                        category: "Languages",
                        details: "JavaScript (ES6+), TypeScript, HTML, CSS, Sass"
                    },
                    {
                        icon: "Figma.png",
                        title: "UI/UX Designer",
                        description: "Crafting user-centered designs balancing aesthetics and functionality for optimal usability.",
                        category: "Tools",
                        details: "Figma, Photoshop, Prototyping"
                    },
                    {
                        icon: "Training.png",
                        title: "Mentor",
                        description: "Guiding developers and designers with practical insights and real-world project experience.",
                        category: "Specializations",
                        details: "Code Reviews, Problem-Solving, Career Guidance"
                    }
                ]
            }
        },
        zh: {
            translation: {
	              // .. 其他翻譯
                cardsData: [
                    {
                        icon: "React.png",
                        title: "前端開發者",
                        description: "構建引人入勝的響應式網站,將想法轉化為無縫的用戶體驗。",
                        category: "語言",
                        details: "JavaScript (ES6+), TypeScript, HTML, CSS, Sass"
                    },
                    {
                        icon: "Figma.png",
                        title: "UI/UX 設計師",
                        description: "創造以用戶為中心的設計,平衡美觀與功能性,實現最佳可用性。",
                        category: "工具",
                        details: "Figma, Photoshop, Prototyping"
                    },
                    {
                        icon: "Training.png",
                        title: "導師",
                        description: "為開發者和設計師提供實用的建議和實際項目經驗。",
                        category: "專業",
                        details: "代碼審查、問題解決、職業指導"
                    }
                ]
            }
        },
    },
		//...其他設定
});
Card 元件實作接著,我們來實作一個 Card 元件,這個元件會根據傳入的動態數據來渲染。
import React from 'react';
import PropTypes from 'prop-types';
import * as styles from '@/components/cards/Card.module.scss';
const Card = ({ icon, title, description, category, details }) => {
    return (
        <div className={styles.card}>
            <div className={styles.cardHeader}>
                <img src={icon} alt={`${title} icon`} className={styles.cardIcon} />
            </div>
            <div className={styles.cardBody}>
                <h3 className={styles.cardTitle}>{title}</h3>
                <p className={styles.cardDescription}>{description}</p>
            </div>
            <div className={styles.cardFooter}>
                <p className={styles.cardCategory}>{category}</p>
                <p className={styles.cardDetails}>{details}</p>
            </div>
        </div>
    );
};
Card.propTypes = {
    children: PropTypes.string,
    title: PropTypes.string,
    description: PropTypes.string,
    category: PropTypes.string,
    details: PropTypes.string,
};
export default Card;
ServiceSection 元件實作在這一步,我們將實作 ServiceSection,這個元件的主要功能是渲染多個 Card 元件 並顯示於服務區塊。它會從 i18n 中動態獲取不同語言下的卡片數據,並將數據通過 Card 元件顯示出來。此外,還可以為此區塊設置一個 id,以便稍後的平滑滾動功能。
// src/components/ServiceSection/ServiceSection.jsx
import React from 'react';
import { useTranslation } from 'react-i18next';
import Card from '@/components/cards/Card';
import * as styles from '@/pages/Portfolio/ServiceSection.module.scss';
const ServiceSection = () => {
    const { t } = useTranslation();
    const services = t('cardsData', { returnObjects: true });  // 獲取服務數據
    return (
        <div id="services" className={styles.serviceSection}>
            <h2 className={styles.title}>Services we’re providing
                that derive 99% result</h2>
            <div className={styles.services}>
                {services.map((service, index) => (
                    <Card
                        key={index}
                        icon={require(`@/assets/icons/${service.icon}`)}
                        title={service.title}
                        description={service.description}
                        category={service.category}
                        details={service.details}
                    />
                ))}
            </div>
        </div>
    );
};
export default ServiceSection;
說明:
useTranslation 函數動態獲取 cardsData,能讓頁面根據不同語言環境動態展示卡片內容。map() 方法迭代 cardsData 數據,並使用 Card 元件來渲染每張卡片。borderSVG 傳入不同的邊框樣式,增強視覺效果。ServiceSection最後,我們將 ServiceSection 元件嵌入到整個 Portfolio 頁面中,這個頁面包含多個區塊(如 HeroSection 和 StatsSection)。我們將統一管理這些區塊的 id,以便未來能夠實現頁面間的平滑滾動。
// src/pages/Portfolio/Portfolio.jsx
import React from 'react';
import HeroSection from '@/components/HeroSection/HeroSection';  
import StatsSection from '@/components/Cards/StatsSection';  
import ServiceSection from '@/components/ServiceSection/ServiceSection';  // 引入ServiceSection
import styles from './Portfolio.module.scss';
const Portfolio = () => {
    return (
        <>
            <HeroSection />
            <StatsSection />
            <ServiceSection />  {/* 使用ServiceSection */}
        </>
    );
};
export default Portfolio;
說明:
id 來統一管理頁面中各個區塊(如 HeroSection 和 ServiceSection),未來可用於實現平滑滾動功能。在這篇文章中,我們展示了如何使用動態數據生成響應式的 Card 元件,並通過 i18n 來支持多語言數據管理。這樣的結構讓網站內容靈活且易於維護。同時,我們遵循了 Mobile First 的設計思路,確保卡片在各種裝置上都能完美顯示。
在下一篇文章中,我們將深入探討 Grid Layout 的使用方式,並展示如何使用 CSS Grid 來實現卡片的響應式佈局。如果你對前端的數據管理和部署感興趣,請繼續關注我們的後續文章!
✨ 流光館Luma<∕> ✨ 期待與你繼續探索更多技術知識!