React: حلول التصميم في React

آخر تحديث: 2026-08-26

أثناء صيانة مشروع كبير للخدمات الخلفية للتجارة الإلكترونية، لاحظ توم أنه مع توسع الفريق، أصبحت حالات تضارب أسماء الفئات في ملف CSS العام أكثر تكرارًا. فقد كان تغيير نمط زر واحد قد يؤثر على العرض في ثلاث صفحات مختلفة. وكان بحاجة إلى حل يمكنه «عزل» الأنماط حتى لا تتداخل مع بعضها البعض.


1. ما ستتعلمه



2. الرسوم التخطيطية المفاهيمية

يوضح الشكل أدناه الاختلافات الرئيسية بين مناهج التصميم الثلاثة من حيث «أسلوب البرمجة» و«سلوك البرنامج أثناء التشغيل»:

100%
flowchart LR
    A[React Design Concepts] --> B[CSS Modules]
    A --> C[Tailwind CSS]
    A --> D[styled-components]

    B --> B1["*.module.css Documents"]
    B --> B2["Compile to Generate a Unique Hash Class Name"]
    B --> B3["Natural Barrier / Zero Runtime"]

    C --> C1["Tool Sets className"]
    C --> C2["PurgeCSS Remove unused styles"]
    C --> C3["Build at Compile Time / Zero Runtime"]

    D --> D1["JS Styling Template Strings"]
    D --> D2["Runtime Injection style Tags"]
    D --> D3["Props Driving Dynamic Styles"]

    style B fill:#e3f2fd,stroke:#1565c0
    style C fill:#e8f5e9,stroke:#2e7d32
    style D fill:#fff3e0,stroke:#e65100


3. سيناريو واقعي

نهج التصميم العزل العبء الإضافي أثناء التشغيل التصميم الديناميكي التوافق مع SSR
الأنماط المضمنة عدم العزل لا شيء ✅ كتابة جافا سكريبت مباشرة
وحدات CSS عزل الهاش لا شيء ❌ يتطلب ربط اسم الفئة
Tailwind CSS بدون ضغط (PurgeCSS) لا شيء ⚠️ تركيبات أسماء الفئات الشرطية
styled-components عزل أسماء الفئات الفريدة نعم (الحقن أثناء وقت التشغيل) ✅ مدفوعة بالخصائص ⚠️ تتطلب تكوين SSR

يقوم فريق توم بصيانة نظام خلفي يضم أكثر من 50 صفحة. في البداية، كانوا يستخدمون ملف CSS عامًا، وكانت قواعد تسمية الفئات تستند بالكامل إلى نهج BEM. وعندما بدأ خمسة مطورين للواجهة الأمامية العمل على المشروع في وقت واحد، ظهرت المشكلات التالية:

قرر توم اتباع نهج تصميم قائم على الهندسة لمعالجة هذه المشكلات. فقد جرب أولاً «CSS Modules»، ثم اختبر «Tailwind CSS»، وأخيراً أنشأ نظام التصميم باستخدام «styled-components». وفيما يلي شرح تفصيلي كامل لهذه الأساليب الثلاثة.


(1) وحدات CSS — عزل الأنماط بدون تكلفة

تُعد وحدات CSS أسهل حل لعزل الأنماط يمكن البدء باستخدامه في مشاريع React. فهي تقوم بترجمة كل ملف CSS إلى وحدة تحتوي على اسم فئة فريد مُجزَّأ، مما يقضي بشكل جذري على تعارضات أسماء الفئات.

المبدأ الأساسي: عندما تكتب .button، يتم ترجمتها إلى .Button_button_abc123، وبالتالي لا يمكن للمكونات الأخرى أن تصادفها عن غير قصد.

كتابة ملفات وحدات CSS

CSS
/* Button.module.css */
.button {
  padding: 10px 20px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.2s ease;
}

.primary {
  background: #1890ff;
  color: white;
}

.primary:hover {
  background: #40a9ff;
}

.danger {
  background: #ff4d4f;
  color: white;
}

.danger:hover {
  background: #ff7875;
}

.default {
  background: #f0f0f0;
  color: #333;
}

.default:hover {
  background: #d9d9d9;
}

الاستيراد والاستخدام في المكونات

JSX
import styles from './Button.module.css'

function Button({ variant = 'primary', children, onClick }) {
  return (
    <button
      className={`${styles.button} ${styles[variant]}`}
      onClick={onClick}
    >
      {children}
    </button>
  )
}

export default Button
▶ جرّب الكود

ناتج DOM المُجمَّع: <button class="Button_button_1a2b3c Button_primary_4d5e6f">Submit</button>؛ اسم الفئة فريد على مستوى النظام.

أفضل الممارسات لتسلسل أسماء الفئات ديناميكيًا

عندما يحتاج أحد المكونات إلى إنشاء أسماء فئات بناءً على مجموعة من الشروط المتعددة، نوصي باستخدام مكتبتَي clsx أو classnames لتجنب التداخل المفرط لسلاسل القوالب:

BASH
npm install clsx
JSX
import styles from './Button.module.css'
import clsx from 'clsx'

function Button({ variant = 'primary', size = 'medium', disabled, children }) {
  return (
    <button
      className={clsx(
        styles.button,
        styles[variant],
        styles[size],
        { [styles.disabled]: disabled }
      )}
      disabled={disabled}
    >
      {children}
    </button>
  )
}
▶ جرّب الكود
CSS
/* Button.module.css — Add New Size Variants */
.small { padding: 4px 12px; font-size: 12px; }
.medium { padding: 8px 20px; font-size: 14px; }
.large { padding: 12px 28px; font-size: 16px; }
.disabled { opacity: 0.5; cursor: not-allowed; }

▶ المثال 1: دمج أسماء فئات متعددة باستخدام وحدات CSS

JSX
import styles from './Card.module.css'

function Card({ title, children, isHighlighted }) {
  return (
    <div
      className={
        `${styles.card} ${isHighlighted ? styles.highlighted : ''}`
      }
    >
      <h3 className={styles.title}>{title}</h3>
      <div className={styles.content}>{children}</div>
    </div>
  )
}
▶ جرّب الكود
CSS
/* Card.module.css */
.card {
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  padding: 16px;
  background: #fff;
}

.highlighted {
  border-color: #1890ff;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}

.title {
  font-size: 16px;
  font-weight: 600;
  margin-bottom: 8px;
  color: #1a1a1a;
}

.content {
  font-size: 14px;
  color: #666;
  line-height: 1.6;
}

الحالات المناسبة: المشاريع الصغيرة إلى المتوسطة الحجم؛ الفرق التي تضم العديد من الأعضاء الجدد؛ المشاريع التي تسعى إلى عزل الأنماط بأقل تكلفة ممكنة.


(2) Tailwind CSS — تطوير سريع وقائم على العناصر الأساسية

يوفر Tailwind CSS مجموعة من أسماء فئات CSS «الذرية» (التي تركز على الأغراض أولاً). ولا يحتاج المطورون إلى كتابة كود CSS مخصص؛ بل يقومون ببناء واجهة المستخدم من خلال دمج الفئات الذرية في JSX.

الفكرة الرئيسية: بدلاً من تسمية الفئات، استخدم فئات مساعدة واضحة من الناحية الدلالية مثل p-4 (padding: 16px)، وtext-lg (font-size: 18px)، وbg-blue-500.

التثبيت والتكوين

BASH
npm install -D tailwindcss @tailwindcss/vite
JS
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [react(), tailwindcss()],
})
CSS
/* index.css */
@import "tailwindcss";

▶ المثال 2: إنشاء مكون «Card» باستخدام Tailwind CSS

JSX
function ProductCard({ product, onAddToCart }) {
  return (
    <div className="border border-gray-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow bg-white">
      <img
        src={product.image}
        alt={product.name}
        className="w-full h-48 object-cover rounded-md mb-3"
      />
      <h3 className="text-lg font-semibold text-gray-800 mb-1">
        {product.name}
      </h3>
      <p className="text-sm text-gray-500 mb-2 line-clamp-2">
        {product.description}
      </p>
      <div className="flex items-center justify-between">
        <span className="text-xl font-bold text-blue-600">
          ${product.price}
        </span>
        <button
          onClick={() => onAddToCart(product)}
          className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
        >
          Add to Cart
        </button>
      </div>
    </div>
  )
}
▶ جرّب الكود

مزايا Tailwind:

حالات الاستخدام: النماذج الأولية السريعة، ومشاريع الشركات الناشئة، والمشاريع متوسطة إلى كبيرة الحجم التي تتبنى فيها الفرق نهج التصميم الذري الموحد.


(3) styled-components — نظام تصميم CSS-in-JS

يُعد «styled-components» أحد أبرز تطبيقات نهج «CSS-in-JS»، حيث تُكتب الأنماط باستخدام سلاسل القوالب في لغة جافا سكريبت، وترتبط هذه الأنماط ارتباطًا وثيقًا بالمكونات.

التثبيت

BASH
npm install styled-components

▶ المثال 3: نظام الأزرار ذو السمات في styled-components

JSX
import styled, { ThemeProvider } from 'styled-components'

// Define the Topic
const theme = {
  colors: {
    primary: '#1890ff',
    success: '#52c41a',
    danger: '#ff4d4f',
    text: '#333',
    textLight: '#fff',
  },
  radii: {
    sm: '4px',
    md: '8px',
    lg: '12px',
  },
  fonts: {
    body: "'Inter', sans-serif",
  },
}

// Styled Components — Automatic Receipt theme prop
const StyledButton = styled.button`
  padding: 10px 24px;
  border: none;
  border-radius: ${props => props.theme.radii.md};
  font-family: ${props => props.theme.fonts.body};
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;

  /* props Conditional Styles Driven by Rules */
  background: ${props => {
    if (props.$variant === 'danger') return props.theme.colors.danger
    if (props.$variant === 'success') return props.theme.colors.success
    return props.theme.colors.primary
  }};
  color: ${props => props.theme.colors.textLight};

  &:hover {
    opacity: 0.85;
    transform: translateY(-1px);
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
  }
`

const ButtonGroup = styled.div`
  display: flex;
  gap: 12px;
  padding: 20px;
`

function App() {
  return (
    <ThemeProvider theme={theme}>
      <ButtonGroup>
        <StyledButton $variant="primary">Main Buttons</StyledButton>
        <StyledButton $variant="success">Success Button</StyledButton>
        <StyledButton $variant="danger" disabled>Danger(Disable)</StyledButton>
      </ButtonGroup>
    </ThemeProvider>
  )
}

الميزات الرئيسية لـ styled-components:

حالات الاستخدام: أنظمة التصميم واسعة النطاق، والسيناريوهات التي تتطلب التبديل الديناميكي بين السمات، وتطوير مكتبات مكونات واجهة المستخدم.



4. مقارنة بين خيارات الحلول

البعد وحدات CSS Tailwind CSS styled-components
منحنى التعلم منخفض (CSS القياسي) متوسط (يتطلب حفظ أسماء الفئات) متوسط (يتطلب فهم CSS-in-JS)
عزل الأنماط العزل الكامل (هاش) لا شيء (أسماء الفئات العامة) العزل الكامل (أسماء الفئات الفريدة)
العبء الإضافي أثناء التشغيل لا شيء لا شيء نعم (يُدرج علامة style)
الأنماط الديناميكية التسلسل عبر جافا سكريبت className دمج أسماء الفئات الشرطية مدفوعة مباشرةً بخصائص (props)
نظام القوالب يتطلب حلاً إضافياً متغيرات CSS + التهيئة مدمج في ThemeProvider
إخراج البناء ملف CSS منفصل الحجم الأدنى بعد استخدام PurgeCSS مضمن في حزمة JS
الحجم الأمثل للمشروع صغير إلى متوسط متوسط إلى كبير كبير/نظام تصميم


5. توصيات بشأن اختيار النموذج

تفاصيل الفريق/المشروع الحل الموصى به
فرق مكونة من 2 إلى 5 أشخاص، ومشاريع صغيرة إلى متوسطة الحجم CSS Modules (الطريقة الأكثر فعالية من حيث التكلفة لتحقيق العزل)
تحتاج فرق الشركات الناشئة إلى عمليات تحسين سريعة Tailwind CSS (لا حاجة لكتابة CSS، وتطوير أسرع)
مشاريع مؤسسية واسعة النطاق، تصميم الأنظمة styled-components (نظام سمات شامل)
ترحيل المشاريع الحالية الحفاظ على النهج الحالي واستبدال الوحدات الجديدة تدريجيًا بوحدات CSS
تطوير مكتبة المكونات styled-components أو CSS Modules (أي منهما مقبول)

▶ المثال 4: أنماط مكونات CSS Modules

JSX
// Button.module.css
// .primary { background: #1677ff; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; }
// .danger { background: #ff4d4f; color: white; }
// .outline { background: transparent; border: 1px solid #1677ff; color: #1677ff; }
// .small { padding: 4px 8px; font-size: 12px; }
// .disabled { opacity: 0.5; cursor: not-allowed; }

import styles from './Button.module.css'

function Button({ variant = 'primary', size, disabled, children, ...props }) {
  const classNames = [
    styles[variant],
    size === 'sm' && styles.small,
    disabled && styles.disabled,
  ].filter(Boolean).join(' ')

  return (
    <button className={classNames} disabled={disabled} {...props}>
      {children}
    </button>
  )
}

function ButtonDemo() {
  return (
    <div style={{ display: 'flex', gap: 8 }}>
      <Button>Primary</Button>
      <Button variant="danger">Delete</Button>
      <Button variant="outline" size="sm">Small Outline</Button>
      <Button disabled>Disabled</Button>
    </div>
  )
}
▶ جرّب الكود

▶ المثال 5: شبكة بطاقات متجاوبة باستخدام Tailwind CSS

TSX
function ProductGrid({ products }) {
  return (
    <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 p-4">
      {products.map(product => (
        <div key={product.id} className="bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 overflow-hidden">
          <div className="h-48 bg-gray-200 flex items-center justify-center">
            <span className="text-gray-400 text-4xl">📦</span>
          </div>
          <div className="p-4">
            <h3 className="font-semibold text-gray-800 truncate">{product.name}</h3>
            <p className="text-sm text-gray-500 mt-1">{product.category}</p>
            <div className="flex items-center justify-between mt-3">
              <span className="text-lg font-bold text-red-500">${product.price}</span>
              <button className="bg-blue-500 hover:bg-blue-600 text-white text-sm px-3 py-1 rounded transition-colors">
                Add to Cart
              </button>
            </div>
          </div>
        </div>
      ))}
    </div>
  )
}


❓ أسئلة شائعة

س أيهما يتمتع بأداء أفضل، CSS Modules أم CSS-in-JS؟
ج توفر CSS Modules أداءً أفضل لأنها تُنشئ ملفات CSS ثابتة أثناء عملية البناء، مما يزيل العبء الإضافي في وقت التشغيل. أما Styled-Components فتقوم بإدراج علامات style في DOM أثناء العرض، مما يؤدي إلى عبء إضافي طفيف أثناء العرض الأول. بالنسبة لمعظم التطبيقات، يكون الفرق ضئيلًا، لكن CSS Modules تتمتع بميزة في سيناريوهات الرسوم المتحركة التي تتطلب أداءً عاليًا.
س ماذا يمكنني أن أفعل إذا كانت أسماء فئات Tailwind CSS طويلة جدًّا وتؤثر على قابلية قراءة الكود؟
ج يمكنك استخدام التوجيه @apply لاستخراج عدة فئات مساعدة وتحويلها إلى فئات CSS مخصصة، أو استخدام مكتبة clsx للتسلسل الشرطي. بالإضافة إلى ذلك، توفر معظم برامج التحرير (مثل VS Code المزود بـ Tailwind CSS IntelliSense) ميزة الإكمال التلقائي لأسماء الفئات ومعاينة عند التمرير بالماوس، لذا لا تشكل قابلية قراءة أسماء الفئات مشكلة في عملية التطوير الفعلية.
س لماذا تحمل $variant في styled-components البادئة $؟
ج البادئة $ هي جزء من اصطلاح «الخاصية المؤقتة» (transient prop) الذي تم تقديمه في styled-components الإصدار 5.2 وما بعده، وهو يشير إلى أن هذه الخاصية تُستخدم فقط لحسابات الأنماط ولن يتم تمريرها إلى عناصر DOM الأساسية. وبدون البادئة $، سيتم عرض الخاصية كسمة HTML (مثل <button variant="primary">)، مما يؤدي إلى ظهور تحذير في وحدة التحكم.
س هل يمكنني الجمع بين عدة حلول للتصميم داخل نفس مشروع React؟
ج نعم، وهذا أمر شائع جدًّا. النهج الموصى به هو استخدام Tailwind CSS للتخطيطات العامة (لإعداد الإطار بسرعة)، وCSS Modules للمكونات التجارية (لضمان الاستقرار والموثوقية)، وstyled-components للمكونات الأساسية لنظام التصميم (لتصميم السمات بمرونة). لا تتعارض هذه الحلول الثلاثة مع بعضها البعض لأنها تستخدم آليات تجميع مختلفة.
س هل تتسبب تقنية CSS-in-JS في عبء إضافي ملحوظ أثناء التشغيل؟ وهل تؤثر على الأداء؟
ج إن تقنية CSS-in-JS أثناء التشغيل (مثل styled-components) تتسبب بالفعل في بعض العبء الإضافي — فهي تُنشئ أسماء الفئات (classNames) وتُدرج علامات <style> في كل مرة يتم فيها عرض المكون. ومع ذلك، فإن التأثير الفعلي عادةً ما يكون ضئيلًا للأسباب التالية: ① لقد خفف العرض المتزامن في React 18 بالفعل من حجب العرض؛ ② تنشأ معظم معوقات الأداء من منطق JavaScript وليس من حسابات الأنماط؛ ③ إذا واجهت مشكلات في الأداء، فيمكنك التبديل إلى حل لا يتطلب وقت تشغيل (مثل vanilla-extract أو Panda CSS).

📖 ملخص


📝 تمارين

  1. إنشاء مكون Avatar باستخدام CSS Modules: يدعم هذا المكون خاصيتين، هما size (صغير/متوسط/كبير) وshape (دائري/مربع)، حيث تتوافق الأحجام الثلاثة مع عروض وارتفاعات مختلفة.
  2. أعد كتابة مكون «Avatar» المذكور أعلاه باستخدام Tailwind CSS، مع مراعاة التصميم المتجاوب: استخدم small على الأجهزة المحمولة، وسيتم التبديل تلقائيًا إلى medium على أجهزة الكمبيوتر المكتبية.
  3. استخدم مكتبة styled-components لإنشاء مكون Badge يتحكم في الرقم المعروض عبر الخاصية count؛ وإذا تجاوز الرقم 99، فاعرض "99+"؛ واستخدم الخاصية theme.colors لتعيين ألوان مختلفة.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%