بنينا نفس التطبيق في SolidJS و React: الإشارات مقابل الـ Hooks
في الربع الماضي، كان لدينا مشروع عميل أعطانا العذر المثالي لفعل شيء كنا نتوق لتجربته: بناء نفس التطبيق في كل من SolidJS و React، ثم مقارنة كل شيء. ليس تطبيق قائمة مهام. ليس عداد. لوحة تحكم إنتاج حقيقية مع المصادقة وتحديثات البيانات في الوقت الفعلي والنماذج المعقدة ووحدة الرسوم البيانية التي تعرض آلاف نقاط البيانات.
فاجأتنا النتائج. ليس لأن إطار عمل واحد "فاز" -- بل لأن المقايضات كانت أكثر دقة بكثير مما يقترحه نقاش Twitter. بعض الأشياء كنا نتوقعها. بعض الأشياء فاجأتنا تماماً. هذا ما تعلمناه بالفعل.
جدول المحتويات
- التطبيق الذي بنيناه
- الإشارات مقابل Hooks: تحول في النموذج الذهني
- مقارنة حجم الحزمة
- معايير أداء العرض
- تجربة المطور والنظام البيئي
- المقايضات الإنتاجية التي تهم فعلاً
- متى تختار SolidJS بدلاً من React
- مقارنة الكود: الأنماط الحقيقية
- الأسئلة الشائعة

التطبيق الذي بنيناه
التطبيق هو لوحة تحكم تحليلات في الوقت الفعلي لعميل التجارة الإلكترونية. إليك ما يتضمنه:
- تدفق المصادقة مع رموز JWT ومنطق التحديث
- لوحة التحكم مع 6 لوحات أدوات، كل منها يسحب البيانات من نقاط نهاية API مختلفة
- خلاصة الطلبات في الوقت الفعلي باستخدام اتصالات WebSocket
- الرسوم البيانية التفاعلية التي تعرض 5000+ نقطة بيانات (باستخدام مكتبة رسوم بيانية)
- نماذج التصفية المعقدة مع القوائم المنسدلة التابعة ومنتقيات نطاق التاريخ
- لوحة إعدادات المسؤول مع إدارة الحالة المتداخلة
- تخطيط سريع الاستجابة مع التنقل في الشريط الجانبي
يتصل كلا الإصدارين بنفس واجهة برمجة التطبيقات الخلفية. كلاهما يستخدم TypeScript. كلاهما يستخدم Vite كأداة البناء. احتفظنا بالمكتبات الخارجية متشابهة قدر الإمكان -- نفس مكتبة الرسوم البيانية (Chart.js)، نفس عميل HTTP (ky)، نفس غلاف WebSocket.
يستخدم إصدار React في React 19 مع hooks. يستخدم إصدار SolidJS في Solid 1.9. لم نستخدم أي ميتا-إطار عمل (لا Next.js، لا SolidStart) لأننا أردنا عزل الاختلافات في الإطار دون أن تعقد التوجيه وSSR المقارنة.
الإشارات مقابل Hooks: تحول في النموذج الذهني
هذا هو الشيء الكبير. وبصراحة، استغرق فريقنا حوالي أسبوع لنتوقف عن كتابة كود على شكل React في SolidJS.
كيفية عمل React Hooks
أنت تعرف هذا، لكن من الجدير بالذكر بوضوح للمقارنة. نموذج React هو: عندما تتغير الحالة، تعاد تنفيذ دالة المكون. الدالة بأكملها. كل useState, كل useMemo, كل useCallback -- إنها آليات للعمل حول حقيقة أن الدالة بأكملها تعيد التنفيذ.
// React: يتم إعادة تنفيذ هذه الدالة بأكملها عند كل تغيير حالة
function OrderFeed() {
const [orders, setOrders] = useState([]);
const [filter, setFilter] = useState('all');
// يعاد حساب هذا في كل عملية عرض إلا إذا قمنا بـ memoize
const filteredOrders = useMemo(() =>
orders.filter(o => filter === 'all' || o.status === filter),
[orders, filter]
);
// يتم إعادة إنشاء هذا ref من الناحية المفاهيمية في كل عملية عرض
const handleNewOrder = useCallback((order) => {
setOrders(prev => [order, ...prev]);
}, []);
useEffect(() => {
const ws = connectWebSocket(handleNewOrder);
return () => ws.close();
}, [handleNewOrder]);
return <OrderList orders={filteredOrders} />;
}
كيفية عمل Solid Signals
نموذج Solid مختلف بشكل أساسي. دالة المكون تعمل مرة واحدة. بعد ذلك، فقط التعبيرات المحددة التي تقرأ إشارة تعاد تنفيذها عندما تتغير تلك الإشارة. لا توجد virtual DOM diff. لا توجد تسوية. الرسم البياني التفاعلي يتتبع بالضبط أي عقد DOM تعتمد على أي إشارات.
// Solid: هذه الدالة تعمل مرة واحدة فقط
function OrderFeed() {
const [orders, setOrders] = createSignal([]);
const [filter, setFilter] = createSignal('all');
// يتم تتبع هذا تلقائياً -- لا حاجة لمصفوفة التبعيات
const filteredOrders = createMemo(() =>
orders().filter(o => filter() === 'all' || o.status === filter())
);
// لا حاجة لـ useCallback -- هذا الإغلاق مستقر
const handleNewOrder = (order) => {
setOrders(prev => [order, ...prev]);
};
// onMount بدلاً من useEffect مع deps فارغة
onMount(() => {
const ws = connectWebSocket(handleNewOrder);
onCleanup(() => ws.close());
});
return <OrderList orders={filteredOrders()} />;
}
ما يعنيه هذا عملياً
إصدار Solid لا يحتوي على مصفوفات تبعيات. لا useCallback. لا useMemo للحالة المشتقة (على الرغم من أن createMemo موجود للحسابات المكلفة -- الفرق هو أنه تحسين اختياري، وليس مطلوباً للصحة).
إليك ما قضمنا أثناء التطوير:
فك الخصائص يقتل التفاعلية في Solid. كان لدينا مطور صغير يفكك الخصائص في مكون Solid وقضينا 45 دقيقة في تصحيح سبب عدم نشر التحديثات. في React، فك الخصائص يمنع حسن التصرف لأن الدالة بأكملها تعيد التنفيذ. في Solid، تحتاج إلى الوصول إلى
props.valueداخل JSX أو نطاق مُتتبع.العودة المبكرة صعبة في Solid. لا يمكنك فعل
if (!data()) return <Loading />في الجزء العلوي من مكون Solid بالطريقة التي ستفعلها في React. دالة المكون تعمل مرة واحدة فقط، لذلك سيتم تقييم هذا الشرط أبداً. تحتاج إلى<Show when={data()}>بدلاً من ذلك.لا توجد أخطاء إغلاق قديمة. كانت هذه الفوز الكبير. في إصدار React الخاص بنا، كان لدينا ثلاثة أخطاء إغلاق قديمة منفصلة في الأسبوع الأول تتعلق بمعالجات WebSocket التي تلتقط حالة قديمة. إصدار Solid كان له صفر، لأن الإشارات تُقرأ دائماً في وقت الوصول، وليس في إغلاق.
مقارنة حجم الحزمة
قسنا كلا الإصدارات الإنتاجية مع نفس تكوين Vite وضغط gzip واستراتيجية تقسيم الكود.
| المقياس | React 19 | SolidJS 1.9 | الفرق |
|---|---|---|---|
| وقت التشغيل للإطار | 44.2 KB (gzip) | 7.1 KB (gzip) | -84% |
| إجمالي الحزمة الأولية | 127.8 KB | 89.3 KB | -30% |
| إجمالي التطبيق (جميع الأجزاء) | 312.4 KB | 274.1 KB | -12% |
| الجزء الأول (المسار الحرج) | 68.4 KB | 41.2 KB | -40% |
| عدد الأجزاء | 14 | 14 | نفس |
وقت تشغيل Solid أصغر بشكل كبير. هذا ليس بأخبار -- لقد تحدث Ryan Carniato عن هذا على نطاق واسع. لكن ما فاجأنا هو أن إجمالي حجم التطبيق اختلف بشكل ملحوظ بمجرد إضافة كود التطبيق الحقيقي والمكتبات الخارجية والأصول.
وقت تشغيل الإطار يهم أكثر للحمل الأولي. وفي 7.1 KB gzipped، يختفي Solid بشكل أساسي في الضوضاء. يعتبر React's 44 KB ملحوظاً، خاصة على اتصالات الهاتف المحمول.
Tree Shaking
يتم tree shaking في Solid بشكل أفضل من React. تم حذف بدائل Solid غير المستخدمة بالكامل. مع React، يتم شحن المصالح والعمارة بالألياف سواء كنت تستخدم جميع ميزاتها أم لا. أكدنا هذا من خلال بناء صفحة الحد الأدنى في كلاهما -- الكلمة الأساسية Solid أقل.

معايير أداء العرض
قمنا بتشغيل معايير منظمة باستخدام ملفات تعريف أداء Chrome DevTools و Lighthouse والتوقيت الآلي المخصص. جميع الاختبارات على MacBook Pro M2 مع إبطاء وحدة المعالجة المركزية المعينة على بطء 4x لمحاكاة الأجهزة متوسطة النطاق.
عرض الرسم البياني (5000 نقطة بيانات)
| المقياس | React 19 | SolidJS 1.9 |
|---|---|---|
| العرض الأولي | 142ms | 89ms |
| إعادة العرض عند تغيير التصفية | 67ms | 23ms |
| الذاكرة أثناء العرض | 18.4 MB | 11.2 MB |
| عقد DOM المنشأة | 5,847 | 5,812 |
كان Solid أسرع باستمرار في إعادة العرض لأنه لا يختلف بشكل virtual DOM tree. عندما تتغير إشارة التصفية، يتم تحديث التعبيرات التي تقرأ تلك الإشارة فقط. ساعدت تحسينات مترجم React 19 -- الاختبار نفسه على React 18 كان 95ms لإعادة العروض -- لكن Solid كان لا يزال لديه ميزة واضحة.
خلاصة الطلبات في الوقت الفعلي (100 تحديث/ثانية)
هذا هو المكان الذي أصبحت فيه الأمور مثيرة للاهتمام حقاً. لقد دفعنا 100 رسالة WebSocket في الثانية إلى خلاصة الطلبات.
| المقياس | React 19 | SolidJS 1.9 |
|---|---|---|
| انخفاض الإطار (لكل 10s) | 12 | 2 |
| متوسط وقت الرسم | 8.3ms | 3.1ms |
| أقصى وقت رسم | 34ms | 11ms |
| استخدام CPU (متوسط) | 24% | 9% |
قام Solid بسحق هذا المعيار بشكل مطلق. التحديثات عالية التكرار هي حيث يدفع التفاعل الحبيبي الدقيق أرباحاً كبيرة. كان React يقوم ببطء التحديثات (وهو جيد)، لكن لا يزال يختلف أكثر من DOM اللازم في كل دفعة.
يجب أن نلاحظ: React 19's useTransition والتجميع التلقائي جعل هذا أفضل بكثير مما كان عليه React 18. وبالنسبة لمعظم تطبيقات العالم الحقيقي، فإنك لا تدفع 100 تحديث في الثانية. في 10 تحديثات/ثانية، كان كلا الإطاران سلساً.
نموذج معقد يحتوي على 40 حقل
| المقياس | React 19 | SolidJS 1.9 |
|---|---|---|
| تأخر إدخال بدون لمس | 2-4ms | <1ms |
| عرض إرسال النموذج | 28ms | 12ms |
| إعادة عرض المكون لكل ضغطة مفتاح | 3-8 | 1 |
في React، سيؤدي الكتابة في حقل واحد إلى إعادة عرض مكونات الأصل ما لم نقم بـ memoize كل شيء بعناية. في Solid، الكتابة في حقل تحرف حرفياً فقط نص عقدة DOM لهذا الحقل. لا يشيء آخر يعاد تنفيذه.
تجربة المطور والنظام البيئي
الأداء ليست كل شيء. عليك أن تبني الشيء فعلياً، وتصحح أخطاءه، وتوظف له، وتحتفظ به.
حجم النظام البيئي (اعتباراً من أوائل 2025)
| العامل | React | SolidJS |
|---|---|---|
| عمليات التنزيل الأسبوعية على npm | ~28M | ~85K |
| نجوم GitHub | 233K+ | 33K+ |
| أسئلة Stack Overflow | 470K+ | ~2K |
| مكتبات مكونات واجهة المستخدم | 50+ خيار ناضج | 5-8 خيارات |
| منشورات الوظائف (LinkedIn US) | ~45,000 | ~200 |
| ميتا-إطار عمل | Next.js (ناضج) | SolidStart (بيتا) |
هذا هو الفيل في الغرفة. النظام البيئي React أكبر بأوامر من حيث الحجم. عندما احتجنا إلى منتقي نطاق التاريخ لـ Solid، كان علينا إما نقل واحد من React أو كتابة واحد بأنفسنا. في React، كان لدينا 15 خياراً للاختيار من بينها.
استخدمنا Kobalte لبدائل واجهة المستخدم التي يمكن الوصول إليها في Solid، وهي جيدة حقاً. لكنها ليست Radix UI من حيث تغطية المكونات.
تصحيح الأخطاء
React DevTools ناضج وجيد الصيانة وشيء يعرفه كل مطور واجهة أمامية. Solid لها أداة DevTools الخاصة بها، وهي لائقة -- يمكنك فحص الرسم البياني الإشارة، الذي يكون أكثر فائدة بالفعل لتتبع أخطاء التفاعلية من عرض الشجرة المكون React. لكنها أقل تلميعاً.
دعم TypeScript
كلاهما ممتاز. دعم TypeScript في Solid أفضل قليلاً لـ JSX لأن المترجم يتعامل مع المزيد وقت البناء. كان لدينا عدد أقل من جمباز النوع في قاعدة كود Solid.
المقايضات الإنتاجية التي تهم فعلاً
بعد بناء كلا الإصدارين ونشر إصدار Solid في التدريج (اختار العميل في النهاية React للإنتاج -- المزيد عن ذلك أدناه)، إليك المقايضات التي أهمت فعلاً:
التوظيف
فريق العميل يتكون من 8 مطورين في الواجهة الأمامية. الكل يعرفون React. لا أحد استخدم Solid. تقدير وقت التدريب: 2-3 أسابيع للإنتاجية، 2-3 أشهر للإتقان. هذه تكلفة حقيقية. هذا هو أكبر عامل فردي في معظم قرارات الإنتاج، وهذا السبب في أننا عادة نوصي بـ React أو الأطر مثل Next.js للفرق التي تحتاج إلى التوظيف بشكل متكرر.
توافق مكتبة جهات خارجية
كان علينا كتابة تغليفات مخصصة لثلاث مكتبات كان لها تكاملات محددة React. أضاف هذا تقريباً 20 ساعة من وقت التطوير. في مشروع React، لا توجد هذه الساعات.
عرض من جانب الخادم
SolidStart واعدة لكنها كانت لا تزال في الإصدار التجريبي خلال مشروعنا. Next.js تمت اختباره في المعركة. لمشاريع حيث نحتاج إلى SSR بدرجة إنتاج مع جميع الزينة، نحن لا نزال نصل إلى تطوير Next.js أو Astro اعتماداً على نموذج المحتوى.
الأداء حيث يهم
إليك الحقيقة الصادقة: بالنسبة لهذه لوحة التحكم المحددة، أداء كلا الإطاران جيداً بما يكفي للإنتاج. كان إصدار Solid أسرع بشكل قابل للقياس، لكن إصدار React لم يكن أبداً بطيء. المستخدمون لن يلاحظوا الفرق في معظم التفاعلات.
الاستثناء كان الخلاصة في الوقت الفعلي عند ترددات التحديث العالية. إذا كان تطبيقك يحتوي على حالة استخدام مثل تلك، فإن ميزة أداء Solid ذات مغزى، وليس فقط مفاخرة المعيار.
متى تختار SolidJS بدلاً من React
بعد هذه التجربة، إليك إطار عملنا الصادق للقرار:
اختر SolidJS عندما:
- يحتوي تطبيقك على تحديثات تفاعلية عالية التكرار (لوحات تحكم وأنظمة التداول والتعاون في الوقت الفعلي)
- حجم الحزمة حرج (أدوات مضمنة وميكروفروندز وموجهة نحو الهاتف المحمول)
- فريقك صغير وراغب في الاستثمار في منحنى التعلم
- تريد التفاعل الحبيبي الدقيق دون محاربة الإطار
- أنت تبني شيء جديد ولا تحتاج إلى نظام بيئي كبير لمكتبة المكونات
اختر React عندما:
- فريقك يعرف بالفعل React (هذا وحده عادة ما يكون حاسماً)
- تحتاج إلى ميتا-إطار عمل ناضج (Next.js, Remix)
- توفر مكتبة جهات خارجية مهمة
- أنت تتوظف وتحتاج إلى حوض موهوب كبير
- لا تحتاج تطبيقك إلى متطلبات أداء نموذجية (ليس متطرفة)
لمشاريع تطويرنا headless CMS development، React لا تزال تهيمن لأن تكاملات CMS (Sanity, Contentful, Storyblok) لها SDKs من الدرجة الأولى في React. يوجد دعم Solid لكنه يتم الحفاظ عليه غالباً من قبل المجتمع.
مقارنة الكود: الأنماط الحقيقية
دعنا نلقي نظرة على بعض الأنماط الحقيقية من المشروع جنباً إلى جنب.
جلب البيانات مع حالات التحميل
React:
function Dashboard() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchDashboardData()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
if (loading) return <Skeleton />;
if (error) return <ErrorBanner error={error} />;
return <DashboardGrid data={data} />;
}
SolidJS:
function Dashboard() {
const [data] = createResource(fetchDashboardData);
return (
<Suspense fallback={<Skeleton />}>
<ErrorBoundary fallback={(err) => <ErrorBanner error={err} />}>
<DashboardGrid data={data()} />
</ErrorBoundary>
</Suspense>
);
}
ـ createResource في Solid مع Suspense أنيقة حقاً. React لديها Suspense أيضاً (وقد تحسنت)، لكن نسخة Solid بدت أكثر طبيعية في العمل معها. قلل من الكود المعياري، أقل متغيرات الحالة للإدارة.
العرض الشرطي
React:
{user ? (
<Profile user={user} />
) : (
<LoginPrompt />
)}
SolidJS:
<Show when={user()} fallback={<LoginPrompt />}>
{(u) => <Profile user={u()} />}
</Show>
مكون <Show> في Solid موجود لأن العمليات الثلاثية لا تعمل بنفس الطريقة عندما تعمل دالة المكون مرة واحدة فقط. استغرقت الأمر بعض الوقت للاعتياد عليها، لكن نمط الاستدعاء يمنحك إشارة مضيقة غير فارغة وهي لطيفة لـ TypeScript.
عرض القوائم
React:
{orders.map(order => (
<OrderRow key={order.id} order={order} />
))}
SolidJS:
<For each={orders()}>
{(order) => <OrderRow order={order} />}
</For>
لا تحتاج <For> في Solid إلى مفاتيح لأنها تتبع عناصر الصفيف بالمرجع. عندما ينتقل عنصر في الصفيف، ينقل Solid عقدة DOM الفعلية. سيقوم React بفك الشيء وإعادة تثبيته. هذا هو السبب في أن أداء قائمة Solid جيدة جداً.
الأسئلة الشائعة
هل SolidJS جاهز للإنتاج في 2025؟ نعم. كان Solid 1.x مستقراً لأكثر من سنتين. استخدمت شركات مثل Cloudflare و Samsung Solid في الإنتاج. المكتبة الأساسية ناضجة واختبار جيداً. السؤال ليس ما إذا كان Solid جاهزاً -- إنه ما إذا كان فريقك ومتطلبات مشروعك متوافقة مع حجم نظامه البيئي.
هل يمكنني الهجرة من React إلى SolidJS بشكل متدرج؟ ليس بسهولة. على الرغم من تشابه بناء جملة JSX، فإن نماذج وقت التشغيل مختلفة بشكل أساسي. لا يمكنك تضمين مكونات Solid داخل تطبيق React (أو العكس) دون حدود iframe أو مكون ويب. كانت الهجرة بشكل أساسي إعادة كتابة. إذا كنت تفكر في الأمر، ابدأ بميزة معزولة جديدة أو ميكروفروندز بدلاً من محاولة تحويل كود React الموجود.
هل يدعم SolidJS عرض من جانب الخادم؟ نعم. يوفر SolidStart SSR والبث وميزات الخادم. وهي وظيفية وتتحسن بسرعة، لكن اعتباراً من أوائل 2025، فهي لا تزال أقل نضجاً من Next.js أو Remix. لمشاريع SSR الثقيلة، سنقيم هل لا تزال توصي بتقييم Next.js أو Astro اعتماداً على احتياجات المحتوى الخاصة بك.
كيف يقارن مترجم React 19 بنهج Solid's؟ mترجم React 19 (المعروف سابقاً باسم React Forget) يقوم بـ memoize تلقائياً للمكونات لتقليل إعادة العروض غير الضرورية. إنه تحسين كبير. لكنه لا يزال يعمل ضمن نموذج React لإعادة تشغيل دوال المكونات والتمييز بـ virtual DOM. يتخطى Solid كلا الخطوتين تماماً. المترجم يجعل React أسرع، لكنه لا يغير العمارة الأساسية. في معاييرنا، كان React 19 مع المترجم أسرع بحوالي 30% من React 18، لكن لا يزال أبطأ من Solid في سيناريوهات محملة بالتحديثات.
ماذا عن Preact Signals كوسط طريقة؟
Preact Signals (و @preact/signals-react adapter) يجلب التفاعلية التي تشبه الإشارة إلى النظام البيئي React. إنها نهج مثيرة للاهتمام، لكنها تحارب نموذج React الأساسي بدلاً من العمل معه. اختبرنا بإيجاز ووجدنا حالات حدية مع Suspense والميزات المتزامنة. إذا كنت تريد إشارات، فإن Solid يمنحك التجربة الكاملة دون عدم التطابق.
هل SolidJS أصعب التعلم من React؟
إذا كنت تعرف بالفعل React، فإن واجهة برمجة التطبيقات الخاصة بـ Solid ستبدو مألوفة -- JSX مشابه، أنماط مشابهة. الجزء الصعب هو عدم التعلم من نموذج React. ستصل إلى أنماط useEffect التي لا تعلم. ستفكك الخصائص وتكسر التفاعلية. ستحاول العودة المبكرة وتتساءل لماذا لا تعمل. الميزانية حوالي 1-2 أسبوع لمطور React ذو خبرة ليصبح منتجاً في Solid، وشهر أو شهرين آخرين للتوقف عن كتابة Solid بنكهة React.
أي إطار لديه دعم TypeScript أفضل؟
كلاهما لديهم دعم TypeScript ممتاز. Solid لديها ميزة طفيفة لأن المترجم يمكنه توفير أنواع أضيق في أنماط معينة (مثل الاستدعاء في <Show>), وأنت لا تحتاج نفس حجم معاملات الأنواع الجنرية التي تتطلبها hooks معقد React أحياناً. لكن بصراحة، كلاهما رائع. لا ينبغي أن يكون هذا عاملاً حاسماً.
هل يجب أن أستخدم SolidJS لمشروعي التالي؟ هذا يعتمد على قيودك. إذا كان فريقك صغيراً يبني شيء حساس للأداء وأنت مستعد للاستثمار في منحنى التعلم والعمل حول نظام بيئي أصغر، فإن Solid هو خيار ممتاز حقاً. إذا كنت بحاجة إلى توظيف مطوري React واستخدام مكتبات مكونات محددة أو بحاجة إلى ميتا-إطار عمل مثبت للإنتاج، فإن React هو الرهان الأكثر أماناً. لا توجد إجابة عامة -- فقط الإجابة الصحيحة لموقفك المحدد. إذا كنت تريد مناقشة القرار لمشروعك، تواصل معنا ويمكننا مساعدتك في تقييم المقايضات.