التحضير للوظيفة
أساسيات تصميم أنظمة الواجهة الأمامية
مقدّمة عملية لتصميم أنظمة الواجهة الأمامية للمقابلات والعمل الحقيقيّ — إطارٌ قابل للتكرار (المتطلّبات، والبنية عالية المستوى، ونموذج البيانات، وواجهة API وتدفّق البيانات، والغوص العميق، والمقايضات)، والمجالات الأساسية المتوقّع أن تفكّر فيها (استراتيجية العرض، وبنية المكوّنات والحالة، وجلب البيانات والتخزين، والأداء، وسهولة الوصول، والوقت الفعليّ)، ومثالٌ تطبيقيّ لتصميم بحثٍ بإكمالٍ تلقائيّ — مع أخطاء شائعة وتمارين.
تصميم أنظمة الواجهة الأمامية هو حيث تُظهِر أنك تستطيع هندسة ميزةٍ أو تطبيق، لا مجرّد كتابة مكوّن — الجولة التي تفصل المتوسّط عن الكبير. مفتوحٌ بطبيعته: "صمّم خلاصة أخبار"، "صمّم إكمالًا تلقائيًّا"، "صمّم معرض صور". الفخّ هو الغوص في الكود؛ والمهارة قيادة محادثةٍ منظّمة عن المتطلّبات والبنية والمقايضات. تمنحك هذه التدوينة إطارًا قابلًا للتكرار والمجالات المتوقّع أن تفكّر فيها. تستند إلى السلسلة كلها — الأداء، وReact، وجلب البيانات، والعرض في Next.js.
النموذج الذهني: إجابة تصميم أنظمة الواجهة الأمامية محادثةٌ منظّمة، لا رسمٌ بيانيّ. قُدها: وضّح المتطلّبات ← ارسم البنية عالية المستوى ← عرّف عقد البيانات وAPI ← اغُص عميقًا في 2–3 مجالاتٍ يهتمّ بها المُقابِل ← اذكر المقايضات. نادرًا ما يوجد جوابٌ واحد صحيح؛ تُقيَّم على كيفية استدلالك، وتحديد النطاق، وتبرير القرارات.
إطارٌ تستطيع اتّباعه دائمًا
أيًّا كان السؤال، امشِ هذه الخطوات بصوتٍ مسموع. البنية نصف الدرجة.
- وضّح المتطلّبات. لا تبدأ التصميم حتى تعرف ما تبنيه. اسأل عن الاحتياجات الوظيفية (ماذا يستطيع المستخدمون؟) وغير الوظيفية (الحجم، أهداف الأداء، الأجهزة، العمل دون اتصال، سهولة الوصول، التعدّد اللغويّ). أعِد صياغتها.
- حدّد النطاق. المشكلة دائمًا أكبر من الوقت. اتّفق على ما بالداخل والخارج — "سأركّز على عرض الخلاصة وجلب البيانات، وأذكر المصادقة بإيجاز". تحديد النطاق الصريح إشارة أقدمية.
- البنية عالية المستوى. ارسم القطع الكبرى: شجرة المكوّنات، وتقسيم العميل/الخادم، ومن أين تأتي البيانات، وكيف تتدفّق. أبقِها نظرة صناديق وأسهمٍ قبل أيّ تفصيل.
- نموذج البيانات وعقد API. ما شكل البيانات؟ ما النقاط النهائية (أو استعلامات GraphQL) التي تحتاجها الواجهة، وبأيّ معاملات (ترقيم، مرشِّحات)؟ تصميم العقد يفرض القرارات المهمّة.
- الغوص العميق. اختر 2–3 مجالاتٍ الأهمّ لـهذه المشكلة واغُص عميقًا — سيوجّهك المُقابِل. هنا الإشارة الحقيقية (أدناه).
- المقايضات والمتابعات. اختم بذكر ما ستحسّنه تاليًا، وما ضحّيت به، وكيف ستعالج الحجم والأخطاء والحالات الحدّية.
المجالات الأساسية للتفكير فيها
هذه الأبعاد التي يمسّها تصميم الواجهة الأمامية. لن تغطّيها كلها كل مرّة — اختر ما يتطلّبه السؤال.
استراتيجية العرض
أين ومتى يُنتَج HTML؟ CSR (على العميل) للواجهات التفاعلية الشبيهة بالتطبيقات خلف تسجيل دخول؛ وSSR للصفحات لكل طلبٍ أو المخصّصة التي تحتاج تحسين بحث؛ وSSG للمحتوى نادر التغيّر (تسويق، توثيق)؛ وISR للساكن مع طزاجةٍ دورية. برّر بطزاجة المحتوى واحتياجات البحث — وهو بالضبط نموذج العرض في Next.js.
بنية المكوّنات والحالة
قسّم الواجهة إلى شجرة مكوّنات، وقرّر أين تعيش الحالة. حالة محلّية لمكوّنٍ واحد؛ ومرفوعة للأشقّاء؛ وسياق للهموم على مستوى التطبيق (السمة، المستخدم)؛ ومخزن (Zustand/Redux) لحالة العميل المشتركة المعقّدة؛ ومكتبة حالة خادم (TanStack Query) لبيانات الخادم. التمييز المفتاح: حالة الخادم مقابل حالة العميل — خلطهما أكثر أخطاء البنية شيوعًا.
جلب البيانات والتخزين
كيف تصل البيانات للشاشة؟ قرّر الجلب على العميل مقابل الخادم، والترقيم مقابل التمرير اللانهائيّ (والمؤشّر مقابل الإزاحة)، والتخزين وإعادة التحقّق، والتحديثات التفاؤلية للتعديلات. استدلّ على دورة حياة الطلب: حالات التحميل/الخطأ/الفراغ، والتسابق، والإلغاء — نموذج جلب البيانات.
الأداء
في النطاق دائمًا تقريبًا. تحدّث عن حجم الحزمة (تقسيم الكود، تحميل المسارات/المكوّنات كسولًا)، وكلفة العرض (المحاكاة الافتراضية للقوائم الطويلة، التحفيظ للأشجار الفرعية المكلفة)، وتحسين الأصول (صور متجاوبة، صيغ حديثة، تحميل الخطوط)، والشبكة (تخزين، تحميل مسبق، debounce للطلبات)، ومؤشّرات الويب الأساسية (LCP، CLS، INP) كأهدافٍ قابلة للقياس. قُل كيف ستقيس، لا ماذا ستفعل فقط.
سهولة الوصول والتعدّد اللغويّ
الإجابة الأقدم تعاملهما كمواطنين من الدرجة الأولى: HTML دلاليّ وتشغيل بلوحة المفاتيح، وARIA حين يلزم، وإدارة التركيز للواجهة الديناميكية (النوافذ، القوائم)، وتباين الألوان — وإن كان ذا صلة، RTL/LTR والمحتوى المترجَم. ذكرها دون سؤالٍ إشارةٌ قويّة.
الوقت الفعليّ والعمل دون اتصال (حين يكون ذا صلة)
للبيانات الحيّة، وازِن الاستطلاع (بسيط، زمن استجابة أعلى) مقابل WebSockets/SSE (وقت فعليّ، بنيةٌ أكثر). وللمرونة، فكّر في استراتيجيات التخزين، وعامل خدمة / PWA للعمل دون اتصال، وكيف توفّق الحالة المحلّية والخادمية عند إعادة الاتصال.
مثال تطبيقيّ: صمّم بحثًا بإكمالٍ تلقائيّ
سؤالٌ مفضّل، لأنه يمسّ مجالاتٍ كثيرة بإيجاز.
وضّح: أثناء الكتابة، أظهِر اقتراحاتٍ من مجموعة بياناتٍ كبيرة. غير وظيفيّ: سريع (< 100ms محسوسًا)، يتحمّل الكتابة السريعة، قابل للوصول، يعمل على الجوّال. النطاق: سأركّز على تدفّق الإدخال←الاقتراحات، والأداء، وسهولة الوصول.
البنية: <input> متحكَّم به، وقائمة اقتراحاتٍ منسدلة، وطبقة بياناتٍ تستعلم واجهة بحث. الحالة: نصّ الاستعلام، والنتائج، والتحميل، والفهرس المُبرَز للتنقّل بلوحة المفاتيح.
تدفّق البيانات والقرارات المفتاحية:
- debounce للإدخال (~200 ملّي ثانية) كي تستعلم حين يتوقّف المستخدم، لا مع كل ضغطة — طلباتٌ أقلّ، وتلعثمٌ أقلّ.
- ألغِ الطلبات القديمة (
AbortController) كي لا تطمس استجابةٌ أبطأ أقدم أحدثَ — التسابق الكلاسيكيّ. - خزّن النتائج حسب الاستعلام كي تكون إعادة كتابة مصطلحٍ سابق فورية (
Mapبسيط، أو مكتبة استعلام). - debounce مقابل throttle: debounce هنا (تصرّف حين يتوقّف الكتابة)؛ وthrottle خطأ (يُطلِق وسط الكتابة).
الأداء: حُدّ النتائج (أعلى ~10)، وحاكِ افتراضيًّا إن طالت القائمة، وتجنّب إعادة عرض القائمة كلها مع كل ضغطة.
سهولة الوصول: نفّذ نمط combobox في ARIA — role="combobox"، وaria-expanded، وaria-activedescendant للخيار المُبرَز، ودعم لوحة مفاتيح كامل (أعلى/أسفل للتنقّل، Enter للاختيار، Escape للإغلاق)، وأعلِن أعداد النتائج لقارئات الشاشة.
المقايضات: الترشيح على العميل (فوريّ، لكن يجب أن تسع البيانات الذاكرة) مقابل البحث على الخادم (يتوسّع، لكن يضيف زمن استجابةٍ ويحتاج آلية debounce/الإلغاء). اذكر أيّهما ولماذا. تلك المشية المنظّمة — وضّح ← صمّم ← تدفّق البيانات ← الغوص ← المقايضات — هي الإجابة.
الأخطاء الشائعة
- الكتابة فورًا — القفز إلى مكوّنٍ قبل توضيح المتطلّبات والنطاق. قُد المحادثة أولًا.
- بلا جمع متطلّبات — التصميم لافتراضاتٍ لم يذكرها المُقابِل. اسأل عن الحجم والأجهزة والبحث والعمل دون اتصال وسهولة الوصول.
- خلط حالة الخادم وحالة العميل — محاولة حفظ بيانات الخادم في
useState/Redux بمزامنةٍ يدوية. استخدم مكتبة حالة خادمٍ لبيانات الخادم؛ وأبقِ حالة العميل منفصلة. - تجاهل حالات التحميل/الخطأ/الفراغ — تصميمٌ يعالج المسار السعيد فقط. الأنظمة الحقيقية تنفق معظم كودها على المسارات غير السعيدة.
- التلويح بالأداء — قول "سأحسّنه" بلا تحديد. اذكر التقنية (المحاكاة الافتراضية، تقسيم الكود، debounce) وكيف ستقيس النتيجة.
- تخطّي سهولة الوصول — معاملتها كاختيارية. للأدوات التفاعلية، نمط ARIA ودعم لوحة المفاتيح جزءٌ من "هل يعمل".
- عدم ذكر المقايضات — تقديم خيارٍ كأنه صحيحٌ بداهةً. لكل قرارٍ حقيقيّ كلفة؛ وذكرها يُظهِر الأقدمية.
تمارين
جرّب كلًّا قبل فتح الحلّ — قُل نهجك بصوتٍ مسموع.
تمرين 1 — التمرير اللانهائيّ مقابل الترقيم
لخلاصةٍ اجتماعية، هل تختار التمرير اللانهائيّ أم صفحاتٍ مرقّمة، وما المقايضة؟
اعرض الإجابة
التمرير اللانهائيّ يناسب خلاصة (تصفّحٌ متّصلٌ مدفوعٌ بالتفاعل) باستخدام ترقيمٍ قائم على المؤشّر (ثابتٌ مع وصول عناصر جديدة، بخلاف الإزاحة). المقايضات: يصعب بلوغ التذييل، ولا ربط عميق لـ"صفحة 5"، وعليك المحاكاة الافتراضية لإبقاء DOM محدودًا. الترقيم أفضل حين يحتاج المستخدمون إيجاد موضعٍ محدّدٍ والعودة إليه (نتائج بحث، جداول). اذكر الخيار ومعه كلفته.
تمرين 2 — أين تعيش حالة السلّة؟
في تطبيق تجارةٍ إلكترونية، أين تحفظ حالة سلّة التسوّق، ولماذا؟
اعرض الإجابة
إنها حالة عميلٍ مشتركة عبر التطبيق (شارة شريط التنقّل، صفحة السلّة، الدفع)، فـمخزنٌ خفيف (Zustand/سياق) — محفوظٌ في localStorage كي يصمد أمام التحديث، ومُزامَنٌ مع الخادم عند الدخول/الدفع. ليست حالة خادمٍ صرفة (تتغيّر دون اتصال، لكل جلسة) ولا محلّية صرفة (مكوّناتٌ كثيرة تقرؤها)، ولهذا بالضبط يناسبها مخزن.
تمرين 3 — اجعل لوحة تحكّمٍ بطيئة تبدو سريعة
لوحة تحكّمٍ بترويسةٍ سريعة وثلاث أدوات بياناتٍ بطيئة. كيف تجعلها تبدو سريعة؟
اعرض الإجابة
دفِّقها: اعرض الترويسة والتخطيط فورًا، وحمّل كل أداةٍ مستقلّةً بهيكلها العظميّ للتحميل (حدود <Suspense> / loading.tsx على الخادم، أو حالات جلبٍ لكل أداةٍ على العميل). يرى المستخدم البنية فورًا وتمتلئ كل أداةٍ حين تُحلّ بياناتها — أداءٌ محسوسٌ أفضل بكثير من الحجب على أبطأ استعلام.
النموذج الذهني الذي تحتفظ به
تصميم أنظمة الواجهة الأمامية محادثةٌ منظّمة: وضّح المتطلّبات (الوظيفية وغير الوظيفية) وحدّد النطاق قبل التصميم، وارسم بنيةً عالية المستوى، وعرّف نموذج البيانات وعقد API، ثم اغُص عميقًا في 2–3 مجالاتٍ تهمّ هذه المشكلة واذكر المقايضات. عبر تلك الغوصات، استدلّ على استراتيجية العرض (CSR/SSR/SSG/ISR حسب الطزاجة والبحث)، وبنية المكوّنات والحالة (وحاسمًا، حالة الخادم مقابل حالة العميل)، وجلب البيانات والتخزين (الترقيم، التسابق، التحديثات التفاؤلية)، والأداء (الحزمة، المحاكاة الافتراضية، مؤشّرات الويب — وكيف ستقيس)، وسهولة الوصول من الدرجة الأولى. نادرًا ما يوجد جوابٌ واحد صحيح — تُقيَّم على كيفية تحديد النطاق والتبرير والمقايضة. قُد المحادثة بهذا الإطار فتحوّل سؤالًا مفتوحًا إلى برهانٍ أنك تفكّر كمهندس، لا كمبرمجٍ فقط.