React: حلول التصميم في React
آخر تحديث: 2026-08-26
أثناء صيانة مشروع كبير للخدمات الخلفية للتجارة الإلكترونية، لاحظ توم أنه مع توسع الفريق، أصبحت حالات تضارب أسماء الفئات في ملف CSS العام أكثر تكرارًا. فقد كان تغيير نمط زر واحد قد يؤثر على العرض في ثلاث صفحات مختلفة. وكان بحاجة إلى حل يمكنه «عزل» الأنماط حتى لا تتداخل مع بعضها البعض.
1. ما ستتعلمه
- كيفية عمل وحدات CSS وآلية العزل المعياري الخاصة بها
- فلسفة التصميم الكامنة وراء أسماء الفئات الذرية في Tailwind CSS
- حل CSS-in-JS الخاص بـ styled-components الذي يعمل أثناء وقت التشغيل
- استراتيجيات الاختيار للخيارات الثلاثة بناءً على حجم المشروع
2. الرسوم التخطيطية المفاهيمية
يوضح الشكل أدناه الاختلافات الرئيسية بين مناهج التصميم الثلاثة من حيث «أسلوب البرمجة» و«سلوك البرنامج أثناء التشغيل»:
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. وعندما بدأ خمسة مطورين للواجهة الأمامية العمل على المشروع في وقت واحد، ظهرت المشكلات التالية:
- تعارض الأنماط: يؤثر
.rowالموجود فيTable.cssعلى.rowالموجود فيOrderList.css - الترابط الشديد: يتطلب تغيير نمط مكون مشترك اختبار جميع الصفحات التي تستخدمه
- تسرب الأنماط: الأنماط المُعرَّفة في الصفحة «أ» تتجاوز بشكل غير متوقع العناصر الموجودة في الصفحة «ب»
قرر توم اتباع نهج تصميم قائم على الهندسة لمعالجة هذه المشكلات. فقد جرب أولاً «CSS Modules»، ثم اختبر «Tailwind CSS»، وأخيراً أنشأ نظام التصميم باستخدام «styled-components». وفيما يلي شرح تفصيلي كامل لهذه الأساليب الثلاثة.
(1) وحدات CSS — عزل الأنماط بدون تكلفة
تُعد وحدات CSS أسهل حل لعزل الأنماط يمكن البدء باستخدامه في مشاريع React. فهي تقوم بترجمة كل ملف CSS إلى وحدة تحتوي على اسم فئة فريد مُجزَّأ، مما يقضي بشكل جذري على تعارضات أسماء الفئات.
المبدأ الأساسي: عندما تكتب .button، يتم ترجمتها إلى .Button_button_abc123، وبالتالي لا يمكن للمكونات الأخرى أن تصادفها عن غير قصد.
كتابة ملفات وحدات 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;
}
الاستيراد والاستخدام في المكونات
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 لتجنب التداخل المفرط لسلاسل القوالب:
npm install clsx
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>
)
}
/* 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
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>
)
}
/* 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.
التثبيت والتكوين
npm install -D tailwindcss @tailwindcss/vite
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
/* index.css */
@import "tailwindcss";
▶ المثال 2: إنشاء مكون «Card» باستخدام Tailwind CSS
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:
- لا حاجة لكتابة ملفات CSS، مما يقلل من الحاجة إلى التبديل بين الملفات
- تُستخدم أسماء الفئات كوصف للأنماط وتتميز بسهولة قراءتها
- عند استخدامه مع PurgeCSS، يمكنه تقليل حجم الناتج بشكل كبير
- للحصول على تصميم متجاوب، ما عليك سوى إضافة البادئة:
md:flex،lg:grid-cols-3
حالات الاستخدام: النماذج الأولية السريعة، ومشاريع الشركات الناشئة، والمشاريع متوسطة إلى كبيرة الحجم التي تتبنى فيها الفرق نهج التصميم الذري الموحد.
(3) styled-components — نظام تصميم CSS-in-JS
يُعد «styled-components» أحد أبرز تطبيقات نهج «CSS-in-JS»، حيث تُكتب الأنماط باستخدام سلاسل القوالب في لغة جافا سكريبت، وترتبط هذه الأنماط ارتباطًا وثيقًا بالمكونات.
التثبيت
npm install styled-components
▶ المثال 3: نظام الأزرار ذو السمات في styled-components
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:
- ترتبط الأنماط ارتباطًا وثيقًا بالمكونات، لذا فمن الطبيعي ألا تنشأ أي تعارضات
ThemeProviderيوفر قوالب عالمية لتسهيل التبديل بين أنظمة التصميم- يمكن استخدام العناصر المساعدة لتنفيذ المنطق الديناميكي في الأنماط
- يدعم
&بناء الجمل المتداخلة، وإطارات الرسوم المتحركة الرئيسية، والأنماط العامةcreateGlobalStyle
حالات الاستخدام: أنظمة التصميم واسعة النطاق، والسيناريوهات التي تتطلب التبديل الديناميكي بين السمات، وتطوير مكتبات مكونات واجهة المستخدم.
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
// 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
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>
)
}
❓ أسئلة شائعة
style في DOM أثناء العرض، مما يؤدي إلى عبء إضافي طفيف أثناء العرض الأول. بالنسبة لمعظم التطبيقات، يكون الفرق ضئيلًا، لكن CSS Modules تتمتع بميزة في سيناريوهات الرسوم المتحركة التي تتطلب أداءً عاليًا.@apply لاستخراج عدة فئات مساعدة وتحويلها إلى فئات CSS مخصصة، أو استخدام مكتبة clsx للتسلسل الشرطي. بالإضافة إلى ذلك، توفر معظم برامج التحرير (مثل VS Code المزود بـ Tailwind CSS IntelliSense) ميزة الإكمال التلقائي لأسماء الفئات ومعاينة عند التمرير بالماوس، لذا لا تشكل قابلية قراءة أسماء الفئات مشكلة في عملية التطوير الفعلية.$variant في styled-components البادئة $؟$ هي جزء من اصطلاح «الخاصية المؤقتة» (transient prop) الذي تم تقديمه في styled-components الإصدار 5.2 وما بعده، وهو يشير إلى أن هذه الخاصية تُستخدم فقط لحسابات الأنماط ولن يتم تمريرها إلى عناصر DOM الأساسية. وبدون البادئة $، سيتم عرض الخاصية كسمة HTML (مثل <button variant="primary">)، مما يؤدي إلى ظهور تحذير في وحدة التحكم.<style> في كل مرة يتم فيها عرض المكون. ومع ذلك، فإن التأثير الفعلي عادةً ما يكون ضئيلًا للأسباب التالية: ① لقد خفف العرض المتزامن في React 18 بالفعل من حجب العرض؛ ② تنشأ معظم معوقات الأداء من منطق JavaScript وليس من حسابات الأنماط؛ ③ إذا واجهت مشكلات في الأداء، فيمكنك التبديل إلى حل لا يتطلب وقت تشغيل (مثل vanilla-extract أو Panda CSS).📖 ملخص
- تحقق وحدات CSS عزل الأنماط من خلال التجزئة في مرحلة التجميع؛ وهي مناسبة للمشاريع الصغيرة والمتوسطة الحجم ولا تتسبب في أي عبء إضافي أثناء التشغيل.
- يستخدم Tailwind CSS أسماء فئات «أتومية» لتوجيه عملية تطوير واجهة المستخدم، وعند دمجه مع PurgeCSS، يصبح الكود الناتج مضغوطًا للغاية.
- توفر مكتبة styled-components أنماطًا ديناميكية تعتمد على الخصائص (props) ونظام السمات ThemeProvider، مما يجعلها مناسبة لأنظمة التصميم واسعة النطاق
- يمكن استخدام هذه الأساليب الثلاثة معًا، مما يتيح مرونة في الاختيار بناءً على نوع المكون ومرحلة المشروع
- بغض النظر عن النهج الذي تختاره، يجب عليك وضع إرشادات وقواعد أسلوب متسقة للفريق.
📝 تمارين
- إنشاء مكون
Avatarباستخدام CSS Modules: يدعم هذا المكون خاصيتين، هماsize(صغير/متوسط/كبير) وshape(دائري/مربع)، حيث تتوافق الأحجام الثلاثة مع عروض وارتفاعات مختلفة. - أعد كتابة مكون «Avatar» المذكور أعلاه باستخدام Tailwind CSS، مع مراعاة التصميم المتجاوب: استخدم
smallعلى الأجهزة المحمولة، وسيتم التبديل تلقائيًا إلىmediumعلى أجهزة الكمبيوتر المكتبية. - استخدم مكتبة styled-components لإنشاء مكون
Badgeيتحكم في الرقم المعروض عبر الخاصيةcount؛ وإذا تجاوز الرقم 99، فاعرض "99+"؛ واستخدم الخاصيةtheme.colorsلتعيين ألوان مختلفة.