إدارة الحالة في Vue 3 للواجهات الإدارية: Pinia أم الحالة المحلية
إدارة الحالة في Vue 3 للواجهات الإدارية: اختر بين Pinia و provide/inject والحالة المحلية مع أمثلة واقعية مثل الفلاتر، المسودات، والتبويبات.

ما الذي يجعل الحالة معقّدة في لوحات الإدارة
لوحات الإدارة تبدو مثقلة بالحالة لأنّها تجمع أجزاء كثيرة على شاشة واحدة. الجدول ليس مجرد بيانات؛ بل يتضمن أيضًا الفرز، الفلاتر، الترقيم الصفحي، الصفوف المحددة، وسياق "ما الذي حدث للتو؟" الذي يعتمد عليه المستخدمون. أضف نماذج طويلة، صلاحيات مبنية على الدور، وإجراءات تغيّر ما تسمح به الواجهة، فتبدأ قرارات الحالة الصغيرة بالمعنى.
التحدّي ليس تخزين القيم. هو الحفاظ على سلوك متوقّع حينما تحتاج عدّة مكوّنات لنفس الحقيقة. إن قالت شريحة الفلتر "نشط"، يجب أن يتفق الجدول، عنوان URL، وعملية التصدير. إذا حرّر المستخدم سجلاً ومَضى بعيدًا، يجب ألا يفقد تطبيقك عمله بصمت. وإذا فتح تبويبتين، لا ينبغي لتبويبة واحدة أن تُبدّل أخرى.
في Vue 3، عادةً ستختار بين ثلاثة أماكن للاحتفاظ بالحالة:
- الحالة المحلية للمكوّن: مملوكة لمكوّن واحد وآمنة لتصفيرها عند إلغاء تركيبه.
provide/inject: حالة مشتركة مقيدة بصفحة أو منطقة ميزة، بدون تمرير خصائص طويل.- Pinia: حالة مشتركة يجب أن تصمد عبر التنقّل، تُعاد استخدامُها عبر المسارات، وتظل سهلة التتبّع.
طريقة مفيدة للتفكير: لكل قطعة من الحالة، قرّر أين يجب أن تعيش حتى تظل صحيحة، لا تفاجئ المستخدم، ولا تتحول إلى سباغيتي.
الأمثلة أدناه تلتصق بثلاثة مشاكل شائعة في الأدمن: الفلاتر والجداول (ما يجب أن يبقى وما يجب أن يُعاد ضبطه)، المسودات والتعديلات غير المحفوظة (نماذج يثق بها المستخدمون)، والتحرير عبر تبويبات متعددة (تجنّب اصطدام الحالة).
طريقة بسيطة لتصنيف الحالة قبل اختيار أداة
تسهل نقاشات الحالة عندما تتوقف عن الجدال حول الأدوات وتُسمي أولًا نوع الحالة. أنواع الحالة المختلفة تتصرّف بشكل مختلف، وخلطها هو ما يخلق أخطاء غريبة.
تقسيم عملي:
- حالة واجهة المستخدم: تبديلات، مربعات منبثقة مفتوحة، الصفوف المحددة، التبويبات النشطة، ترتيب الفرز.
- حالة الخادم: استجابات الـ API، أعلام التحميل، الأخطاء، وقت التحديث الأخير.
- حالة النموذج: قيم الحقول، أخطاء التحقق، أعلام الاتساخ، المسودات غير المحفوظة.
- حالة عبر الشاشات: أي شيء تحتاجه عدة مسارات للقراءة أو التغيير (مساحة العمل الحالية، صلاحيات مشتركة).
ثم عرّف النطاق. اسأل أين تُستخدم الحالة اليوم، لا أين قد تُستخدم مستقبلًا. إذا كانت تهم جدولًا واحدًا فقط، فغالبًا تكفي الحالة المحلية. إذا احتاجها مكوّنان أخوان على نفس الصفحة، فالمشكلة الحقيقية هي المشاركة على مستوى الصفحة. إذا احتاجتها عدة مسارات، فأنت في منطقة حالة التطبيق المشتركة.
بعدها العمر الافتراضي. بعض الحالات يجب أن تُعاد ضربتها عند إغلاق درج. حالات أخرى يجب أن تصمد عبر التنقّل (فلاتر أثناء النقر على سجل والعودة). وبعضها يجب أن يصمد عبر إعادة التحميل (مسودة طويلة يعود إليها المستخدم لاحقًا). معاملة الثلاثة بنفس الطريقة تخلق فلاتر تُعاد ضبطها غامضًا أو مسودات تختفي.
أخيرًا، افحص التزامن. لوحات الإدارة تواجه حواف كثيرة بسرعة: يفتح المستخدم نفس السجل في تبويبتين، تحديث خلفي يحدّث صفًا بينما النموذج متسخ، أو يتسابق محرّران للحفظ.
مثال: شاشة "المستخدمون" مع فلاتر، جدول، ودِرج تحرير. الفلاتر هي حالة واجهة مستخدم بمدة صفحة. الصفوف هي حالة خادم. حقول الدرج هي حالة نموذج. إذا تم تحرير نفس المستخدم في تبويتين، تحتاج قرارًا صريحًا حول التزامن: الحظر، الدمج، أم التحذير.
بمجرد أن تستطيع تسمية الحالة بالنوع والنطاق والعمر والتزامن، يصبح اختيار الأداة (local, provide/inject, أو Pinia) أوضح بكثير.
كيف تختار: عملية قرار تصمد
خيارات الحالة الجيدة تبدأ بعادة واحدة: وصف الحالة بكلمات بسيطة قبل اختيار الأداة. لوحات الإدارة تمزج جداول، فلاتر، نماذج كبيرة، وتنقّل بين سجلات، لذلك حتى الحالة "الصغيرة" يمكن أن تتحول إلى مغناطيس للأخطاء.
عملية قرار من 5 خطوات
-
من يحتاج للحالة؟
- مكوّن واحد: اجعلها محلية.
- عدة مكوّنات تحت صفحة واحدة: فكّر في
provide/inject. - عدة مسارات: فكّر في Pinia.
الفلاتر مثال جيد. إذا كانت تؤثر على جدول واحد يملكه فقط، فالحالة المحلية تكفي. إذا كانت الفلاتر في شريط رأس تقود جدولًا أدناه، فمشاركة مقيدة بالصفحة (غالبًا
provide/inject) تحافظ على النظافة. -
كم يجب أن تعيش؟
- إذا كان يمكن أن يختفي عند إلغاء تركيب المكوّن، فالحالة المحلية مثالية.
- إذا يجب أن تصمد بعد تغيير المسار، فغالبًا Pinia خيار أفضل.
- إذا يجب أن تصمد بعد إعادة التحميل، فستحتاج أيضًا للعرض في التخزين (storage)، بغض النظر عن مكان وجودها.
هذا مهم بشكل خاص للمسودات. التعديلات غير المحفوظة حسّاسة للثقة: يتوقع الناس أن تبقى المسودة عند النقر بعيدًا والعودة.
-
هل يجب أن تُشارك عبر تبويبات المتصفح أم تكون معزولة لكل تبويب؟
التحرير المتعدد التبويبات هو المكان الذي تختبئ فيه الأخطاء. إذا كان كل تبويب يجب أن يملك مسودته الخاصة، فتجنّب أحاديّة عالمية. فَضّل الحالة المفهرسة حسب معرف السجل، أو اجعلها مقيدة بالصفحة حتى لا تكتب تبويبة واحدة فوق أخرى.
-
اختر أبسط خيار يناسب.
ابدأ محليًا. ارتقِ فقط عند شعورك بألم حقيقي: تمرير خصائص، منطق مكرّر، أو إعادة ضبط يصعب تكراره.
-
أكد حاجتك للتتبّع والتصحيح.
إن احتجت إلى عرض واضح وقابل للفحص للتغييرات عبر الشاشات، فمزايا Pinia في الإجراءات المركزية وتفتيش الحالة توفر ساعات عمل. إذا كانت الحالة قصيرة العمر وواضحة، فالحالة المحلية أسهل للقراءة.
الحالة المحلية للمكوّن: متى تكفي
الحالة المحلية هي الخيار الافتراضي عندما تهم البيانات مكوّنًا واحدًا في صفحة واحدة. من السهل تجاهل هذا الخيار وبناء مخزن ستقضي شهورًا في صيانته.
مناسبة واضحة هي جدول واحد مع فلاتره. إذا كانت الفلاتر تؤثر على جدول واحد فقط (مثال: قائمة المستخدمين) ولا شيء آخر يعتمد عليها، احتفظ بها كـ ref داخل مكوّن الجدول. نفس الشيء ينطبق على حالة واجهة صغيرة مثل "هل المودال مفتوح؟"، "أي صف يُحرر؟"، و"ما العناصر المحددة الآن؟".
حاوِل ألا تخزن ما يمكنك حسابه. شعار "فلاتر نشطة (3)" يجب أن يُحسب من قيم الفلاتر الحالية. تسميات الفرز، الملخّصات المنسقّة، وعلامات "قابل للحفظ" أفضل كـ computed لأنها تبقى متزامنة تلقائيًا.
قواعد إعادة الضبط أهم من الأداة. قرّر ما يُمحى عند تغيير المسار (عادةً كل شيء)، وما يبقى عندما يغيّر المستخدم العرض داخل نفس الصفحة (قد تحتفظ بالفلاتر لكن تمسح الاختيارات المؤقتة لتجنب إجراءات جمع مفاجئة).
الحالة المحلية عادةً تكفي عندما:
- تؤثر الحالة على وِجِت واحد (نموذج واحد، جدول واحد، مودال واحد).
- لا تحتاج شاشة أخرى لقراءتها أو تغييرها.
- يمكنك إبقاؤها داخل 1-2 مكوّنات دون تمرير خصائص عبر طبقات كثيرة.
- يمكنك وصف سلوك إعادة الضبط في جملة واحدة.
الحدّ الرئيسي هو العمق. عندما تبدأ بتمرير نفس الحالة عبر مكوّنات متداخلة كثيرة، تتحوّل الحالة المحلية إلى تمرير خصائص مملّ، وعادةً هذه إشارة للانتقال إلى provide/inject أو مخزن.
provide/inject: مشاركة الحالة داخل صفحة أو منطقة ميزة
تقع provide/inject بين الحالة المحلية والمخزن الكامل. يوفّر أب القيم لكل ما تحته، وتحقن المكوّنات المتداخلة تلك القيم بدون تمرير خصائص. في لوحات الإدارة، هي مناسبة عندما تنتمي الحالة لشاشة واحدة أو منطقة ميزة وليس للتطبيق كله.
نمط شائع هو غلاف صفحة يملك الحالة بينما تستهلكه مكونات أصغر: شريط فلاتر، جدول، شريط إجراءات جماعية، درج تفاصيل، ولافتة "التعديلات غير المحفوظة". يمكن للغلاف أن يزوّد سطحًا تفاعليًا صغيرًا مثل كائن filters، كائن draftStatus (متّسخ، يحفظ، خطأ)، وبعض العلامات للقراءة فقط (مثل isReadOnly بناءً على الصلاحيات).
ما الذي يجب تقديمه (اجعله صغيرًا)
إذا قدّمت كل شيء، فأنت في الواقع أنشأت مخزنًا بتركيب أقل منظمًا. قدّم فقط ما تحتاجه عدة أطفال حقًا. الفلاتر مثال كلاسيكي: عندما يجب أن يبقى الجدول، الشرائح، إجراء التصدير، والصفحة متزامنة، فمن الأفضل مشاركة مصدر واحد للحقيقة بدل التعامل مع تمرير الخصائص والأحداث.
الوضوح والمخاطر
أكبر خطر هو الاعتماديات المخفية: طفلك "يعمل فقط" لأن شيئًا أعلى قدّمه، وبعدها يصعب معرفة من أين تأتي التحديثات. للحفاظ على القابلية للقراءة والاختبار، أعطِ الحقن أسماء واضحة (غالبًا باستخدام ثوابت أو Symbols). وفضّل تقديم إجراءات بدلاً من كائنات قابلة للتعديل فقط. واجهة API صغيرة مثل setFilter، markDirty، و resetDraft تجعل الملكية والتغييرات المسموح بها صريحة.
Pinia: الحالة المشتركة والتحديثات المتوقعة عبر الشاشات
تتألّق Pinia عندما يجب أن تبقى نفس الحالة متسقة عبر المسارات والمكوّنات. في واجهة إدارة، هذا غالبًا يعني المستخدم الحالي، ما يُسمح له به، مساحة/منظمة مختارة، وإعدادات التطبيق. تصبح المشكلة مؤلمة إذا أعادت كل شاشة تنفيذها.
المخزن يساعد لأنّه يعطيك مكانًا واحدًا للقراءة والتحديث. بدل تمرير الخصائص عبر طبقات، تستورد المخزن حيث تحتاجه. عند الانتقال من قائمة إلى صفحة تفصيل، يمكن لباقي الواجهة أن تتفاعل مع نفس المنظمة المختارة، الصلاحيات، والإعدادات.
لماذا يبدو Pinia أسهل للصيانة
تفرض Pinia تركيبًا بسيطًا: state للقيم الخام، getters للقيم المشتقة، وactions للتحديثات. في واجهات الإدارة، يمنع هذا التركيب "الحلول السريعة" من التحول إلى طفرات متشتتة.
إذا كانت canEditUsers تعتمد على الدور الحالي زائد علم ميزة، ضع القاعدة في getter. إذا تطلب تبديل المنظمة مسح الاختيارات المخبأة وإعادة تحميل التنقّل، ضع هذه السلسلة في action. ستقل ساعتا البحث عن "لماذا تغيّر هذا؟".
يعمل Pinia جيدًا أيضًا مع Vue DevTools. عندما يحدث خطأ، من الأسهل تفتيش حالة المخزن ومعرفة أي إجراء نُفّذ بدل المطاردة عبر كائنات تفاعلية مبعثرة داخل مكوّنات عشوائية.
تجنّب مخزن سلة المهملات
المخزن العالمي يبدو مرتبًا في البداية، ثم يتحول إلى درج فوضوي. المرشحون الجيدون لـ Pinia هم القضايا المشتركة فعليًا مثل هوية المستخدم، الصلاحيات، مساحة العمل المختارة، أعلام الميزات، وبيانات مرجعية مشتركة عبر الشاشات.
هموم الصفحة فقط (مثل مدخلات مؤقتة لنموذج واحد) يجب أن تبقى محلية ما لم تكن بحاجة إليها عبر مسارات فعلًا.
مثال 1: فلاتر وجداول دون تحويل كل شيء إلى مخزن
تخيّل صفحة Orders: جدول، فلاتر (الحالة، نطاق التاريخ، العميل)، ترقيم صفحي، ولوحة جانبية تعرض معاينة للطلب المحدد. هذا يصبح فوضويًا سريعًا لأن من المغري وضع كل فلتر وإعداد جدول في مخزن عالمي.
طريقة بسيطة للاختيار هي تقرير ما يجب تذكره وأين:
- ذاكرة فقط (محلي أو provide/inject): يُعاد ضبطها عند مغادرة الصفحة. مناسبة للحالة القابلة للتصرف.
- معلمات الاستعلام: قابلة للمشاركة وتبقى بعد إعادة التحميل. جيدة للفلاتر والصفحات التي ينسخها الناس.
- Pinia: تصمد عبر التنقّل. جيدة لـ"العودة للقائمة كما تركتها".
من هناك، عادةً يتبع التنفيذ المنطق:
إذا لم يتوقع أحد بقاء الإعدادات بعد التنقل، احتفظ بـ filters, sort, page, و pageSize داخل مكوّن صفحة Orders، وليجعل هذا المكوّن يحفّز الجلب. إذا كان شريط الأدوات، الجدول، ولوحة المعاينة يحتاجون نفس النموذج وكانت تمرير الخصائص مزعجًا، انقل نموذج القائمة إلى غلاف الصفحة وشاركه عبر provide/inject. إذا أردت أن تشعر القائمة بأنها لزجة عبر المسارات (افتح طلبًا، اذهب بعيدًا، عد لنفس الفلاتر والاختيار)، فـ Pinia هو الأنسب.
قاعدة عملية: ابدأ محليًا، انتقل إلى provide/inject عندما يحتاج عدة أطفال لنفس النموذج، واستعن بـ Pinia فقط عند الحاجة الحقيقية لبقاء عبر المسارات.
مثال 2: المسودات والتعديلات غير المحفوظة (نماذج يثق بها المستخدمون)
تصوّر وكيل دعم يعدّل سجل عميل: تفاصيل الاتصال، معلومات الفوترة، وملاحظات داخلية. ينقطع، يغيّر الشاشة، ثم يعود. إن نسي النموذج عمله أو حفظ جزئيًا بيانات غير مكتملة، تفقد الثقة.
للمسودات، فرّق بين ثلاثة أشياء: السجل المحفوظ آخر مرة، تعديلات المستخدم المرحلية، وحالة واجهة فقط مثل أخطاء التحقق.
الحالة المحلية: تعديلات مرحلية مع قواعد اتساخ واضحة
إذا كانت شاشة التحرير مغلقة ذاتيًا، فالحالة المحلية غالبًا أنسب. احتفظ بنسخة draft من السجل، تتبع isDirty (أو خريطة حقول متسخة)، وخزّن الأخطاء بجانب عناصر النموذج.
تدفّق بسيط: جلب السجل، استنساخ إلى draft، تحرير المسودة، وإرسال طلب الحفظ فقط عند ضغط المستخدم حفظ. الإلغاء يلغي المسودة ويعيد التحميل.
provide/inject: مسودة واحدة مشتركة عبر أقسام متداخلة
غالبًا تُقسّم نماذج الإدارة إلى تبويبات أو لوحات (الملف الشخصي، العناوين، الصلاحيات). مع provide/inject يمكنك الاحتفاظ بنموذج مسودة واحد وتعرض API صغيرة مثل updateField(), resetDraft(), و validateSection(). كل قسم يقرأ ويكتب من نفس المسودة دون تمرير الخصائص عبر خمس طبقات.
متى يساعد Pinia مع المسودات
يقع دور Pinia عندما يجب أن تصمد المسودات عبر التنقّل أو تكون مرئية خارج صفحة التحرير. نمط شائع هو draftsById[customerId]، بحيث يحصل كل سجل على مسودته الخاصة. هذا يساعد أيضًا عندما يمكن للمستخدم فتح شاشات تحرير متعددة.
أخطاء المسودات عادةً تأتي من بعض الأخطاء المتوقعة: إنشاء مسودة قبل تحميل السجل، الكتابة فوق مسودة متّسخة عند إعادة الجلب، نسيان مسح الأخطاء عند الإلغاء، أو استخدام مفتاح مشترك واحد يسبّب الكتابة فوق المسودات. إذا ضبطت قواعد واضحة (متى تُنشىء، تٌكتَب فوق، تُتخلّى، تُحفظ، وتُستبدل بعد الحفظ)، تختفي معظم هذه المشكلات.
إن بنيت شاشات الإدارة باستخدام AppMaster (appmaster.io)، يظل الانقسام "المسودة مقابل السجل المحفوظ" قائمًا: احتفظ بالمسودة على العميل، واعتبر الخادم مصدر الحقيقة فقط بعد حفظ ناجح.
مثال 3: التحرير في تبويبات متعددة دون اصطدامات الحالة
التحرير في تبويبات متعددة هو المكان الذي تنهار فيه لوحات الإدارة غالبًا. يفتح المستخدم عميل A ثم عميل B، يعود ويتوقع أن تتذكر كل تبويبة تعديلاتِها غير المحفوظة.
الحل هو نمذجة كل تبويبة كحزمة حالة خاصة بها، لا كمشاركة واحدة. تحتاج كل تبويبة مفتاحًا فريدًا (غالبًا بناءً على معرف السجل)، بيانات المسودة، الحالة (نظيف، متّسخ، يحفظ)، وأخطاء الحقول.
إذا كانت التبويبات ضمن شاشة واحدة، فنهج محلي يعمل جيدًا. احتفظ بقائمة التبويبات والمسودات يملكها مكوّن الصفحة الذي يعرض التبويبات. يقرأ كل محرّر ويكتب فقط حزمه. عند إغلاق تبويبة، امسح الحزمة وانتهى الأمر. هذا يبقي الأمور معزولة وسهلة الفهم.
بغض النظر عن مكان وجود الحالة، الشكل مشابه:
- قائمة كائنات تبويبات (كل واحدة لديها
customerId,draft,status, وerrors) activeTabKey- إجراءات مثل
openTab(id),updateDraft(key, patch),saveTab(key), وcloseTab(key)
يصبح Pinia خيارًا أفضل عندما يجب أن تصمد التبويبات عبر التنقّل (اذهب إلى Orders وارجع) أو عندما تحتاج شاشات متعددة لفتح وتركيز تبويبات. في هذه الحالة، مخزن "مدير التبويبات" الصغير يحافظ على السلوك عبر التطبيق.
أهم اصطدام يجب تجنّبه هو متغير عالمي واحد مثل currentDraft. يعمل حتى تُفتح التبويبة الثانية، ثم تبدأ التعديلات في الكتابة فوق بعضها، وتُعرض أخطاء التحقق في المكان الخطأ، ويحفظ الزر السجل الخطأ. عندما تمتلك كل تبويبة حزمتها، تختفي الاصطدامات بتصميمها.
الأخطاء الشائعة التي تسبب أخطاء وكود فوضوي
معظم أخطاء لوحات الإدارة ليست "أخطاء Vue". هي أخطاء حالة: البيانات تعيش في المكان الخطأ، جزآن من الشاشة يختلفان، أو تبقى حالة قديمة بصمت.
إليك الأنماط التي تظهر غالبًا:
وضع كل شيء في Pinia افتراضيًا يجعل الملكية غير واضحة. المخزن العالمي يبدو منظّمًا في البداية، لكن سرعان ما يقرأ كل صفحة ويكتب نفس الكائنات، ويصبح التنظيف لعبة تخمين.
استخدام provide/inject بدون عقد واضح يولّد اعتماديات مخفية. إذا حقن طفل filters لكن لا يوجد تفاهم مشترك عن من يوفّرها وما هي الإجراءات المسموح بها، ستحصل على تحديثات مفاجئة عندما يبدّل طفل آخر نفس الكائن.
خلط حالة الخادم مع حالة الواجهة في نفس المخزن يسبب الكتابات العرضية. السجلات المجلوبة تتصرف مختلفًا عن "هل الدرج مفتوح؟" أو "أي تبويب نشط؟". عندما تعيش معًا، قد يمحو إعادة الجلب واجهة المستخدم أو تغييرات الواجهة تغير بيانات الكاش.
تجاهل تنظيف دورة الحياة يسمح بتسرب الحالة. فلاتر من عرض واحد قد تؤثر على آخر، والمسودات قد تبقى بعد مغادرة الصفحة. في المرة التالية التي يفتح فيها أحدهم سجلًا مختلفًا، سيرى اختيارات قديمة ويفترض أن التطبيق معطّل.
تأشير المسودات بمفاتيح ضعيفة هي قاتل ثقة هادئ. إن خزّنت المسودات تحت مفتاح واحد مثل draft:editUser، فإن تحرير المستخدم A ثم B يكتب فوق نفس المسودة.
قاعدة بسيطة تمنع معظم هذا: أبقِ الحالة قريبة قدر الإمكان مما تُستخدم له، وارفعها فقط عندما تحتاج حقيقتًا إلى مشاركة بين جزئين مستقلين. وعند المشاركة، عرّف من يملكها (من يغيرها) والهوية (كيف تُفهرَس).
قائمة تحقق سريعة قبل اختيار local أو provide/inject أو Pinia
السؤال الأكثر فائدة: من يملك هذه الحالة؟ إذا لم تستطع قوله في جملة واحدة، فربما الحالة تفعل أكثر من اللازم ويجب تقسيمها.
استخدم هذه الفحوصات كفلتر سريع:
- هل يمكنك تسمية المالك (مكوّن، صفحة، أم التطبيق كله)؟
- هل يجب أن تصمد عبر تغيّر المسار أو إعادة التحميل؟ إذا نعم، خطط للثبات بدل الاعتماد على المتصفح.
- هل سيُحرر سجلان في نفس الوقت أبدًا؟ إذا نعم، ففهرس الحالة حسب معرف السجل.
- هل تُستخدم الحالة فقط من قبل مكوّنات تحت غلاف صفحة واحد؟ إذا نعم،
provide/injectغالبًا يناسب. - هل تحتاج لتفتيش التغييرات وفهم من غيّر ماذا؟ إذا نعم، Pinia غالبًا المكان الأنظف لهذه القطعة.
مطابقة الأدوات، بعبارات بسيطة:
إذا عاشت الحالة وماتت داخل مكوّن واحد (مثل علامة فتح/إغلاق القائمة)، إبقها محلية. إذا احتاجت عدة مكوّنات على نفس الشاشة لسياق مشترك (شريط الفلاتر + جدول + ملخّص)، provide/inject يشاركها بدون جعلها عالمية. إذا يجب أن تُشارك عبر الشاشات، تصمد عبر التنقّل، أو تحتاج تحديثات متوقعة وسهلة التصحيح، استعن بـ Pinia وفهرس المدخلات بمعرف السجل عندما تتعلّق بمسودات.
إذا كنت تبني واجهة Vue 3 إدارية (بما في ذلك مولّدة بواسطة أدوات مثل AppMaster)، تساعدك هذه القائمة على تجنّب وضع كل شيء في مخزن مبكرًا.
خطوات تالية: تطور الحالة بدون خلق فوضى
أأمن طريق لتحسين إدارة الحالة في لوحات الإدارة هو تطويرها بخطوات صغيرة ومملة. ابدأ بالحالة المحلية لأي شيء يبقى داخل صفحة واحدة. عندما ترى إعادة استخدام حقيقية (منطق مكرر، مكوّن ثالث يحتاج نفس الحالة)، ارفعه مستوى واحد. فقط بعد ذلك فكر في مخزن مشترك.
مسار يعمل لمعظم الفرق:
- ابدأ بالحالة الخاصة بالصفحة محليًا أولًا (فلاتر، فرز، ترقيم صفحي، لوحات مفتوحة/مغلقة).
- استخدم
provide/injectعندما تحتاج عدة مكوّنات على نفس الصفحة لسياق مشترك. - أضف مخزن Pinia واحدًا في كل مرة للاحتياجات عبر الشاشات (مدير المسودات، مدير التبويبات، مساحة العمل الحالية).
- اكتب قواعد إعادة الضبط والتزم بها (ماذا يُمحى عند التنقّل، تسجيل الخروج، مسح الفلاتر، تجاهل التغييرات).
قواعد إعادة الضبط تبدو صغيرة، لكنها تمنع معظم لحظات "لماذا تغيّر هذا؟". قرّر مثلاً ماذا يحدث للمسودة عندما يفتح أحدهم سجلًا مختلفًا ثم يعود: استعادة، تحذير، أم إعادة ضبط. ثم اجعل ذلك سلوكًا ثابتًا.
إذا أدخلت مخزنًا، اجعله مُشكّلًا حسب الميزة. مخزن المسودات يجب أن يتعامل مع الإنشاء، الاستعادة، والمسح، لكنه لا يجب أن يملك فلاتر الجداول أو علمية تخطيط واجهة المستخدم.
إذا أردت صنع نموذج إداري بسرعة، AppMaster (appmaster.io) يمكنه توليد تطبيق ويب Vue3 إضافةً إلى الواجهة الخلفية ومنطق العمل، وما زال بإمكانك تعديل الشفرة المولّدة حيث تحتاج معالجة حالة مخصصة. خطوة عملية تالية هي بناء شاشة واحدة من البداية للنهاية (مثل نموذج تحرير مع استرداد المسودات) لترى ما يحتاج فعليًا Pinia مقابل ما يمكن أن يبقى محليًا.
الأسئلة الشائعة
استخدم الحالة المحلية عندما يؤثر البيانات على مكوّن واحد فقط ويمكن مسحها عندما يُزال ذلك المكوّن. أمثلة شائعة: فتح/إغلاق حوار، الصفوف المحددة في جدول واحد، وقسم نموذج لا يعاد استخدامه في مكان آخر.
استعمل provide/inject عندما تحتاج عدة مكوّنات على نفس الصفحة إلى مصدر حقيقة مشترك ويصبح تمرير الخصائص مزعجًا. احتفظ بما تقدمه صغيرًا ومحدّدًا حتى تبقى الصفحة سهلة الفهم.
استعمل Pinia عندما يجب أن تُشترك الحالة عبر المسارات، تبقى بعد التنقّل، أو تحتاج أن تكون سهلة الفحص والتتبّع في مكان واحد. أمثلة شائعة: مساحة العمل الحالية، الصلاحيات، أعلام الميزات، ومدراء عبر الشاشات مثل المسودات أو التبويات.
ابدأ بتسمية نوع الحالة (UI، خادم، نموذج، عبر الشاشات)، ثم حدّد النطاق (مكوّن واحد، صفحة واحدة، عدة مسارات)، المدة (تمسح عند إلغاء التركيب، تبقى عبر التنقل، تبقى بعد إعادة التحميل)، والتزامن (محرّر واحد أم متعدد التبويبات). عادة ما يتبع اختيار الأداة هذه التصنيفات.
إذا توقع المستخدمون مشاركة أو استعادة العرض، ضع الفلاتر والتقسيم في معلمات الاستعلام (query params) لتبقى بعد إعادة التحميل ويمكن نسخها. إذا يتوقعون فقط «العودة للقائمة كما تركتها» عبر التنقل، ضع نموذج القائمة في Pinia؛ وإلا فاجعله خاصًا بالصفحة.
فصل بين السجل المحفوظ آخر مرة وتعديلات المستخدم المرحلية، وأعد الكتابة إلى الخادم فقط عند ضغط حفظ. تتبّع قاعدة واضحة لـ isDirty، وقرّر ماذا يحدث عند التنقل (تحذير، حفظ تلقائي، أو إبقاء مسودة قابلة للاسترداد) حتى لا يفقد المستخدم عمله.
أعطِ كل محرّر مفتوح حزمه الحالة الخاصة به مفاتيحًا تمثّل الهوية (مثل recordId) بدل متغيّر عالمي واحد مثل currentDraft. هذا يمنع أن تكتب تبويبة واحدة على تبويبة أخرى أو أن تظهر أخطاء التحقق في المكان الخطأ.
يمكن أن تعمل المسودات محليًا أو عبر provide/inject إن كان تدفق التحرير محصورًا في مسار واحد. إذا وجب أن تبقى المسودات عند التنقل أو تُعرض خارج شاشة التحرير، فغالبًا ما يكون Pinia مع بنية مثل draftsById[recordId] أبسط وأكثر قابلية للتوقّع.
لا تخزن ما يمكنك اشتقاقه. استخرج الشارات، الملخّصات، وعلامات «قابل للحفظ» باستخدام computed حتى تبقى متزامنة دائمًا ولا تنحرف.
أكبر الأخطاء: وضع كل شيء في Pinia افتراضيًا، خلط استجابات الخادم مع تبديلات واجهة المستخدم، وعدم تنظيف الحالة عند التنقل. راقب مفاتيح المسودات الضعيفة مثل مشاركة مفتاح واحد يعيد استخدامه لعدة سجلات—هذا يقتل ثقة المستخدم.


