كن مطوّر واجهات أمامية محترفا
11 دقيقة قراءة

أسئلة مقابلات الواجهة الأمامية: React وNext.js والأساسيات

بنك منسّق لأسئلة مقابلات الواجهة الأمامية مع إجاباتٍ قويّة موجزة — JavaScript واللغة، وHTML/CSS وسهولة الوصول، والمتصفّح والشبكة، والأداء ومؤشّرات الويب الأساسية، وReact (الخطّافات، العرض، الحالة)، وNext.js (مكوّنات الخادم، العرض، التخزين)، وأسئلة سلوكية وتصميمية — كلٌّ بـ'اعرض الإجابة' تختبر نفسك عليها، إضافةً إلى كيفية التعامل مع المقابلة نفسها.

تكافئ المقابلات الفهم، لا التعريفات المحفوظة. هذا بنكٌ للأسئلة التي تظهر فعلًا لأدوار الواجهة الأمامية وReact وNext.js — كلٌّ بإجابةٍ موجزةٍ قويّة تختبر نفسك عليها (انقر اعرض الإجابة فقط بعد أن تحاول). يقترن بتدريب المسائل البرمجية بـ JavaScript للجولات العملية؛ وهذه التدوينة للجولات المفاهيمية. اعمل خلالها بصوتٍ مسموع، كما ستجيب في الغرفة.

كيف تجيب عن أيّ سؤال مقابلة: ابدأ بالجوهر في جملة، ثم اشرح السبب، ثم أعطِ مثالًا ملموسًا أو مقايضة. المُقابِل يُقيّم هل تفهم الآلية، لا هل تحفظ تعريفًا. "يعتمد" بدايةٌ ممتازة — ما دمت ستقول بعدها على ماذا يعتمد.

JavaScript واللغة

س: ما الفرق بين == و===؟

اعرض الإجابة

== يقارن بعد تحويل الأنواع (تحويل المعاملات إلى نوعٍ مشترك، بقواعد مفاجئة مثل كون 0 == "" صحيحًا)؛ و=== يقارن بلا تحويل — نفس النوع ونفس القيمة. استخدم دائمًا === إلا في اختصار x == null المتعمَّد الذي يلتقط null وundefined. يريد المُقابِل سماع أنك تتجنّب مطبّات التحويل.

س: اشرح الإغلاقات.

اعرض الإجابة

الإغلاق دالّةٌ محزومةٌ مع مراجع للمتغيّرات من حيث عُرّفت — تُبقيها حيّةً بعد عودة الدالّة الخارجية. إنه الآلية وراء الحالة الخاصّة، ودوال المصانع، ومعظم خطّافات React. مثال: makeCounter يُرجِع دالّةً تزيد count الذي تراه وحدها. (المزيد في الإغلاقات والنطاق وthis.)

س: كيف تُحدَّد this؟

اعرض الإجابة

بـكيفية استدعاء الدالّة، لا بمكان تعريفها: obj.method()this هو obj؛ الاستدعاء المجرّد ← undefined (الوضع الصارم) أو الكائن العالميّ؛ وcall/apply/bind تضبطها صراحةً. الدوال السهمية بلا this خاصّة — ترثها من النطاق المحيط، ولهذا هي مثالية لردود النداء داخل التوابع.

س: ما حلقة الأحداث؟ توقّع خرج console.log وsetTimeout(…,0) وPromise.then.

اعرض الإجابة

JavaScript أحادية الخيط بـحلقة أحداث. يعمل الكود المتزامن أولًا، ثم تُفرَّغ طابور المهامّ الدقيقة (ردود نداء الوعود) كاملًا، ثم المهامّ الكبرى (المؤقّتات). فـ console.log("A"); setTimeout(()=>log("B"),0); Promise.resolve().then(()=>log("C")); log("D") يطبع A, D, C, B — الوعد (مهمّة دقيقة) يسبق المؤقّت (مهمّة كبرى) حتى عند 0ms. (راجع async/await.)

س: var مقابل let مقابل const؟

اعرض الإجابة

var مرتبطٌ بالدالّة ومرفوع (يتسرّب من الكتل، ويسبّب علل الحلقات الكلاسيكية)؛ وlet وconst مرتبطان بالكتلة. const يمنع إعادة إسناد الارتباط لكنه لا يجعل الكائنات ثابتة. استخدم const افتراضيًّا، وlet حين تعيد الإسناد، ولا var أبدًا في الكود الجديد.

س: ما الفرق بين null وundefined؟

اعرض الإجابة

undefined يعني "لا قيمة أُسنِدت بعد" (الافتراضيّ للمتغيّرات غير المهيّأة والخصائص الغائبة)؛ وnull "فراغٌ" متعمَّد تُسنِده قصدًا. typeof undefined هو "undefined"؛ وtypeof null هو "object" (علّة تاريخية). null == undefined صحيح، لكن null === undefined خطأ.

س: اشرح map وfilter وreduce.

اعرض الإجابة

كلها تُرجِع مصفوفات/قيمًا جديدة (بلا تغيير). map يحوّل كل عنصرٍ واحدًا لواحد؛ وfilter يُبقي العناصر التي تجتاز شرطًا؛ وreduce يطوي المصفوفة إلى قيمةٍ متراكمة واحدة. تتسلسل، مستبدلةً معظم الحلقات اليدوية بخطّ أنابيبٍ تصريحيّ: orders.filter(o => o.paid).map(o => o.amount).reduce((a,b)=>a+b, 0).

HTML وCSS وسهولة الوصول

س: لماذا يهمّ HTML الدلاليّ؟

اعرض الإجابة

الوسوم الدلالية (<nav>، <main>، <article>، <button>) تصف المعنى، لا المظهر فقط. يمنحك ذلك سهولة الوصول مجّانًا (قارئات الشاشة تعلن الأدوار، ودعم لوحة المفاتيح على <button>)، وتحسين بحثٍ أفضل، ووسومًا أسهل صيانةً. <div onClick> نمطٌ مضادٌّ كلاسيكيّ — ليس قابلًا للتركيز ولا للتشغيل بلوحة المفاتيح؛ أما <button> فكذلك.

س: اشرح نموذج الصندوق في CSS.

اعرض الإجابة

كل عنصرٍ صندوق: المحتوىالحشوالحدّالهامش. box-sizing: content-box (الافتراضيّ) يجعل width المحتوى فقط، فيضيف الحشو/الحدّ إلى الحجم المعروض؛ وbox-sizing: border-box يجعل width يشمل الحشو والحدّ — أكثر توقّعًا بكثير، ولهذا تضبطه معظم إعادات الضبط عالميًّا.

س: Flexbox مقابل Grid — متى تستخدم كلًّا؟

اعرض الإجابة

Flexbox أحاديّ البُعد — رتّب العناصر في صفٍّ أو عمود (أشرطة تنقّل، أشرطة أدوات، توسيط). Grid ثنائيّ البُعد — صفوفٌ وأعمدة معًا (تخطيطات الصفحات، معارض البطاقات، أيّ شيءٍ بمحاذاةٍ في المحورين). يتركّبان: خلية Grid يمكن أن تحتوي حاوية flex. الجأ إلى Grid حين تضع في الاتجاهين.

س: كيف تعمل خصوصية CSS؟

اعرض الإجابة

الخصوصية ترتّب المحدّدات: الأنماط السطرية > المعرّفات > الأصناف/السمات/الأصناف الزائفة > العناصر. المحدّد الأخصّ يفوز بغضّ النظر عن ترتيب المصدر؛ وتُحسَم التعادلات بالقواعد المتأخّرة. !important يتجاوز كل ذلك (تجنّبه — رائحة صيانة). أبقِ الخصوصية منخفضةً ومسطّحة كي تبقى الأنماط متوقّعة.

س: كيف تجعل مكوّنًا قابلًا للوصول؟

اعرض الإجابة

استخدم العناصر الدلالية أولًا (<button>، <a>، <label>+<input> الأصلية)؛ وأضِف ARIA فقط لسدّ الثغرات (role، aria-label، aria-expanded). اضمن التشغيل بلوحة المفاتيح (قابل للتركيز، Enter/Space، حلقة تركيزٍ مرئية)، وتباين ألوانٍ كافٍ، وبدائل نصّية ذات معنى (alt). اختبر بالتنقّل بلوحة المفاتيح وحدها وقارئ شاشة.

المتصفّح والويب

س: ماذا يحدث حين تكتب عنوانًا وتضغط Enter؟

اعرض الإجابة

يحلّ DNS النطاق إلى IP ← يُفتَح اتصال TCP (وTLS) ← يُرسَل طلب HTTP ← يستجيب الخادم بـ HTML ← يحلّله المتصفّح، ويبني DOM، ويجلب CSS/JS/الصور، ويبني CSSOM، ويدمجهما في شجرة العرض، ويحسب التخطيط، ويرسم. JS قد يحجب التحليل؛ وCSS يحجب العرض. أظهِر أنك تعرف مسار العرض الحرج.

س: ما الفرق بين localStorage وsessionStorage والكوكيز؟

اعرض الإجابة

localStorage يبقى حتى المسح (~5–10 ميغابايت، لا يُرسَل للخادم)؛ وsessionStorage مثله لكنه يُمسَح عند إغلاق التبويب؛ والكوكيز صغيرة (~4 كيلوبايت)، تُرسَل مع كل طلبٍ لنطاقها، وهي المكان الصحيح لرمز الجلسة — برايتَي httpOnly (لا تقرؤها JS) وsecure. لا تضع رموز المصادقة في localStorage (قابلة للقراءة عبر XSS).

س: ما CORS؟

اعرض الإجابة

مشاركة الموارد عبر الأصول — آلية أمانٍ في المتصفّح. افتراضيًّا يحجب المتصفّح JS من قراءة استجاباتٍ من أصلٍ مختلف (المخطّط+المضيف+المنفذ). يختار الخادم الدخول بإرسال ترويسات Access-Control-Allow-Origin؛ ولبعض الطلبات يرسل المتصفّح أولًا طلبًا استباقيًّا OPTIONS. يفرض المتصفّح CORS، لا الخادم — نقطة التباسٍ شائعة.

س: إعادة التدفّق مقابل إعادة الطلاء؟

اعرض الإجابة

إعادة التدفّق (التخطيط) تعيد حساب هندسة العناصر — مكلفة، تُطلَق بتغيير الحجم/الموضع أو قراءة خصائص التخطيط وسط الكتابة. إعادة الطلاء تعيد رسم البكسلات بلا تغيير التخطيط (مثلًا تغيير لون) — أرخص. تحريك transform/opacity يتجنّب كليهما (يُركَّبان على GPU)، ولهذا هما الخاصّتان الأداتيّتان للتحريك.

الأداء

س: ما مؤشّرات الويب الأساسية؟

اعرض الإجابة

مقاييس Google المتمحورة حول المستخدم: LCP (أكبر رسم محتوى — التحميل، الهدف < 2.5s)، وCLS (انزياح التخطيط التراكميّ — الثبات البصريّ، < 0.1)، وINP (التفاعل حتى الرسم التالي — الاستجابة، < 200ms). تُترجَم إلى "هل يُحمَّل سريعًا، ويبقى ثابتًا، ويستجيب سريعًا؟" — حسّن الصور/الخطوط لـ LCP، واحجز مساحةً لـ CLS، وأبقِ الخيط الرئيسيّ حرًّا لـ INP.

س: كيف تقلّل زمن التحميل الأوّليّ لتطبيق ويب؟

اعرض الإجابة

قسّم الكود كي ينزّل المستخدمون ما يحتاجه المسار فقط (استيراد ديناميكيّ)؛ وحمّل كسولًا الصور والمكوّنات تحت الطيّة؛ وحسّن الصور (صيغٌ حديثة، أحجامٌ صحيحة) والخطوط (استضافة ذاتية، font-display)؛ وصغّر واضغط JS/CSS؛ وخزّن بقوّة؛ واعرض على الخادم (SSR/SSG) كي لا يُحجَب الرسم الأول على JS. قِس قبل وبعد.

س: ما الفرق بين debounce وthrottle؟

اعرض الإجابة

كلاهما يحدّ كم مرّةً تعمل دالّة. debounce ينتظر حتى يتوقّف النشاط N ملّي ثانية ثم يُطلِق مرّةً — جيّد للبحث أثناء الكتابة. throttle يُطلِق مرّةً على الأكثر كل N ملّي ثانية أثناء النشاط المستمرّ — جيّد لمعالِجات التمرير/التحجيم. debounce يطوي دفعةً في استدعاءٍ أخيرٍ واحد؛ وthrottle يسمح بواحدٍ يمرّ كل فترة.

React

س: ما DOM الافتراضيّ وإعادة التوفيق؟

اعرض الإجابة

تحتفظ React بشجرةٍ خفيفة في الذاكرة (DOM الافتراضيّ). عند تغيّر حالةٍ تعيد تشغيل مكوّناتك لإنتاج شجرةٍ جديدة، تقارنها بالسابقة (إعادة التوفيق)، وتطبّق أدنى تغييرات DOM الحقيقيّ فقط. فـ"إعادة العرض" تعني إعادة تشغيل دوالّك، لا إعادة بناء الصفحة — ولهذا هي سريعة. (راجع أساسيات React.)

س: لماذا تحتاج القوائم key، ولماذا لا فهرس المصفوفة؟

اعرض الإجابة

المفاتيح تتيح لـ React مطابقة العناصر بين العروض لتعرف ما أُضيف/أُزيل/أُعيد ترتيبه، فتحدّث تلك فقط. استخدم معرّفًا فريدًا ثابتًا. أما فهرس المصفوفة فينكسر حين يُعاد ترتيب القائمة أو يُدرَج/يُحذَف منها — فتربط React الحالة الخطأ بالعنصر الخطأ (مثلًا نصّ حقلٍ يقفز صفوفًا). مفاتيح الفهارس آمنةٌ فقط لقائمةٍ ساكنة لا يُعاد ترتيبها.

س: ما قواعد الخطّافات ولماذا وُجدت؟

اعرض الإجابة

استدعِ الخطّافات في المستوى الأعلى فقط (لا في شروطٍ/حلقات) ومن دوال React فقط. تتعقّب React الخطّافات بترتيب الاستدعاء، فيجب أن يكون الترتيب متطابقًا في كل عرضٍ لمطابقة كل استدعاءٍ بحالته المخزّنة. اكسر القاعدة فتختلط الحالة. (راجع الحالة والخطّافات.)

س: كيف تعمل مصفوفة اعتماديات useEffect؟

اعرض الإجابة

يُعاد تشغيل الأثر حين تتغيّر أيّ اعتماديةٍ مذكورة: بلا مصفوفة ← كل عرض؛ [] ← مرّة عند التركيب؛ [a, b] ← حين يتغيّر a أو b. يجب ذكر كل قيمةٍ تفاعلية يقرؤها الأثر، وإلا يعمل بقيمٍ قديمة. أرجِع دالّة تنظيف لإلغاء الاشتراكات/المؤقّتات. علّة شائعة: كائن/مصفوفةٌ حرفية في الاعتماديات تعيد التشغيل إلى الأبد (مرجعٌ جديد كل عرض).

س: متى تحتاج فعلًا useMemo/useCallback؟

اعرض الإجابة

ليس افتراضيًّا — لهما كلفة. استخدمهما لـحسابٍ مكلفٍ حقًّا، أو لإبقاء مرجعٍ ثابت يتطلّبه React.memo أو مصفوفة اعتماديات خطّافٍ آخر. اللجوء إليهما في كل مكانٍ تحسينٌ مبكّر يجعل الكود أصعب قراءةً بلا مكسبٍ ملموس.

س: لماذا يجب ألّا تغيّر الحالة؟

اعرض الإجابة

تقرّر React إعادة العرض بـمقارنة المراجع. تغيير كائن/مصفوفةٍ يُبقي المرجع نفسه، فقد تتخطّى React التحديث ولا تعكس الواجهة التغيير. أنشئ دائمًا قيمةً جديدة (النشر للكائنات/المصفوفات، map/filter للمصفوفات). ولنفس السبب تستخدم مُحدِّثًا دالّيًّا حين تعتمد الحالة التالية على السابقة.

س: المكوّنات المتحكَّم بها مقابل غير المتحكَّم بها؟

اعرض الإجابة

قيمة الحقل المتحكَّم به تقودها حالة React (value + onChange) — React مصدر الحقيقة الوحيد، ما يتيح التحقّق والمنطق الشرطيّ. أما غير المتحكَّم به فيحفظ حالته في DOM، تُقرأ عبر ref عند الحاجة — أبسط، كودٌ أقلّ، جيّد للنماذج البسيطة. يريد المُقابِل المقايضة: التحكّم مقابل البساطة.

س: كيف تشارك الحالة بين المكوّنات؟

اعرض الإجابة

ارفعها إلى أقرب أبٍ مشترك ومرّرها نزولًا (خصائص نزولًا، ردود نداءٍ صعودًا). للحالة التي تحتاجها مكوّناتٌ متناثرة، استخدم السياق (السمة، المستخدم الحاليّ، اللغة). للحالة المعقّدة أو حالة الخادم، الجأ لمكتبة — useReducer/Zustand لحالة العميل، وTanStack Query لحالة الخادم. ابدأ محلّيًّا؛ وصعّد فقط حين يؤلم التنقيط.

س: ما الذي يسبّب إعادة عرضٍ غير ضرورية وكيف تصلحها؟

اعرض الإجابة

يُعاد عرض المكوّن حين تتغيّر حالته أو خصائصه، أو يُعاد عرض أبيه. أسباب شائعة: خصائص كائن/دالّةٍ جديدة كل عرض، وقيمة سياقٍ تتغيّر، وحالةٌ تعيش عاليًا جدًّا. الإصلاحات: حصر الحالة أسفل، وReact.memo لابنٍ نقيّ، وتثبيت الخصائص بـ useMemo/useCallback، وتقسيم السياقات. لكن قِس أولًا — معظم عمليات إعادة العرض رخيصة.

Next.js

س: مكوّنات الخادم مقابل مكوّنات العميل؟

اعرض الإجابة

في موجّه التطبيق، المكوّنات مكوّنات خادمٍ افتراضيًّا — تعمل على الخادم، تجلب البيانات وتستخدم الأسرار مباشرةً، تشحن صفر JS، وترسل HTML. أضِف "use client" فقط للتفاعلية (حالة، آثار، معالِجات أحداث). أفضل ممارسة: أبقِ مكوّنات العميل صغيرةً في الأوراق؛ ومكوّن خادمٍ يجلب ويمرّر البيانات إليها. (راجع أساسيات Next.js.)

س: اشرح SSG وSSR وISR.

اعرض الإجابة

SSG يعرض إلى HTML وقت البناء (الأسرع، للمحتوى نادر التغيّر). SSR يعرض في كل طلب (للبيانات لكل طلب/مستخدم). ISR ساكنٌ لكن يُعاد توليده في الخلفية على فترة (revalidate) — سرعة الساكن مع طزاجةٍ دورية. في موجّه التطبيق لا تضبط هذه مباشرةً غالبًا؛ بل تحدّد خيارات جلب بياناتك (مخزّن مقابل واجهاتٍ ديناميكية) النمط. (راجع العرض والتخزين.)

س: كيف يختلف جلب البيانات في موجّه التطبيق عن React على العميل؟

اعرض الإجابة

في مكوّن خادمٍ تجعل المكوّن async وawait fetch مباشرةً — بلا useEffect، بلا حالة تحميل، بلا شلّالٍ في المتصفّح، وتبقى الأسرار خادمية. تكون البيانات جاهزةً قبل إرسال HTML. أما الجلب على العميل (useEffect + حالة) فللبيانات التي تعتمد على تفاعل المستخدم بعد التحميل. فضّل الجلب على الخادم للبيانات الأولية. (راجع جلب البيانات.)

س: ما فعل الخادم؟

اعرض الإجابة

دالّةٌ غير متزامنة مُعلَّمة بـ "use server" تعمل على الخادم وقابلة للاستدعاء مباشرةً من مكوّناتك (مثلًا <form action={fn}>)، بلا مسار API تبنيه. إنها حقًّا نقطة نهايةٍ عامّة، فيجب أن تصادق وتفوّض وتتحقّق داخلها. بعد التعديل، استدعِ revalidateTag/revalidatePath لتحديث الذواكر. (راجع أفعال الخادم.)

س: صفحةٌ تُظهِر بياناتٍ قديمة بعد تحديث. لماذا، وكيف تصلح؟

اعرض الإجابة

بيانات المسار مخزّنة (ساكنة/ذاكرة البيانات) ولم تُبطَل. أصلِح بإعادة التحقّق: استدعِ revalidateTag("…") أو revalidatePath("/…") في فعل الخادم الذي أجرى التغيير (وسِّم عمليات الجلب المعنية)، أو استخدم router.refresh() لذاكرة موجّه العميل. فهم الذواكر الأربع هو إجابة المستوى الأعلى هنا.

س: متى تستخدم معالِج مسارٍ بدل فعل خادم؟

اعرض الإجابة

معالِجات المسار (route.ts) للـ الخطّافات، والواجهات JSON العامّة التي يستهلكها عملاءُ غير React، وردود OAuth، والتدفّق — أيّ شيءٍ يحتاج نقطة نهاية HTTP قياسية بـ Request/Response خام. ولتعديلات نماذجك، فضّل فعل خادم (كودٌ أقلّ، مُنمَّط، تحسين تدريجيّ). (راجع التوجيه المتقدّم.)

سلوكيّ وتصميميّ

س: كيف تقرّر متى تقسّم مكوّنًا إلى أصغر؟

اعرض الإجابة

قسّم حين يفعل المكوّن أكثر من مهمّة، أو يُعاد استخدام جزءٍ منه، أو يراكم رايات منطقية كثيرة (إشارة للتركيب بدلًا منها)، أو حين تحتاج قطعةٌ حالتها الخاصّة. فضّل التركيب — مكوّناتٌ صغيرة تُدمَج عبر children/الخصائص — على مكوّنٍ عملاقٍ قابل للضبط. أبقِ المكوّنات مركّزة؛ وسمِّها بما تعرضه.

س: حدّثني عن مشكلة أداءٍ حللتها.

اعرض الإجابة

هيكِل الإجابة كـمشكلة ← قياس ← إصلاح ← نتيجة: ما كان بطيئًا (مثلًا قائمة كبيرة تتلعثم عند التمرير)، وكيف قِستها (لوحة الأداء في أدوات المطوّر، تراجع مؤشّر ويب)، والإصلاح (المحاكاة الافتراضية / التحفيظ / تقسيم الكود / نقل الجلب للخادم)، والنتيجة المُكمَّمة (هبط LCP من X إلى Y). يريد المُقابِلون دليلًا أنك تقيس، لا تخمّن.

س: كيف تتعامل مع مهمّة برمجية لست متأكّدًا كيف تحلّها؟

اعرض الإجابة

أعِد صياغة المسألة وأكّد المتطلّبات؛ واسأل عن الحالات الحدّية (فارغة، عنصر واحد، مُدخَل ضخم) بصوتٍ مسموع؛ واذكر نهجًا مباشرًا أولًا (العامل يتفوّق على الذكيّ)؛ ثم نفّذ وأنت تروي تفكيرك؛ ثم اختبر بمثالك وحالةٍ حدّية. إيصال العملية أهمّ من معرفة الجواب فورًا.

النموذج الذهني الذي تحتفظ به

تختبر المقابلات هل تفهم الآليات: حلقة الأحداث والإغلاقات وراء سلوك JavaScript، وHTML الدلاليّ ونموذج الصندوق وراء الواجهة، ومسار العرض الحرج ومؤشّرات الويب وراء الأداء، وDOM الافتراضيّ وقواعد الخطّافات والحالة الثابتة وراء React، وحدّ الخادم/العميل وأنماط العرض والتخزين وراء Next.js. لأيّ سؤال، ابدأ بالجوهر في جملة، واشرح السبب، والجأ إلى مثالٍ ملموسٍ أو مقايضة — "يعتمد، وهذا على ماذا". اقرِن هذا بـتدريبات المسائل البرمجية للجولات العملية، وتدرّب على قول الإجابات بصوتٍ مسموع، فتدخل قادرًا على الاستدلال من الأساسيات بدل الأمل بورود السؤال نفسه بالضبط.