التصفّح بالمؤشر مقابل ترقيم الإزاحة لواجهات API السريعة لشاشات الإدارة
تعلّم الفرق بين التصفّح بالمؤشر والإزاحة مع عقدة API موحدة للفرز والفلاتر والإجماليات تبقي شاشات الإدارة سريعة على الويب والهاتف.

لماذا يمكن أن يجعل الترقيم شاشات الإدارة تبدو بطيئة
شاشات الإدارة غالباً ما تبدأ كجدول بسيط: تحميل أول 25 صفاً، إضافة صندوق بحث، انتهى. يبدو الأمر فورياً مع بضعة مئات من السجلات. ثم تكبر مجموعة البيانات، ويبدأ نفس الشاشة بالتلعثم.
المشكلة المعتادة ليست في واجهة المستخدم. إنها ما يجب على واجهة البرمجة أن تفعله قبل أن تعيد الصفحة 12 مع تطبيق الفرز والفلاتر. مع اتساع الجدول، تقضي الخلفية وقتاً أطول في إيجاد المطابقات، عدّها، وتخطي النتائج الأقدم. إذا كانت كل نقرة تُطلق استعلاماً أثقل، تبدو الشاشة كما لو أنها تفكر بدلاً من أن تستجيب.
تميل إلى ملاحظة ذلك في نفس الأماكن: تغييرات الصفحات تصبح أبطأ مع الوقت، الفرز يصبح بطيئاً، البحث يبدو غير متسق عبر الصفحات، والتمرير اللانهائي يحمل في دفعات (سريع ثم فجأة بطيء). في الأنظمة المزدحمة قد ترى تكرارات أو صفوف مفقودة عندما تتغير البيانات بين الطلبات.
واجهات الويب والهاتف تدفع الترقيم في اتجاهات مختلفة. جدول إدارة على الويب يشجع القفز إلى صفحة محددة والفرز على عدة أعمدة. شاشات الهاتف عادةً تستخدم قائمة لانهائية تحمل الشريحة التالية، ويتوقع المستخدمون أن كل تحميل يكون سريعاً بنفس المقدار. إذا كانت واجهتك مبنية فقط حول أرقام الصفحات، يعاني الهاتف غالباً. إذا بُنيت فقط حول التالي/بعد، قد تبدو جداول الويب محدودة.
الهدف ليس مجرد إرجاع 25 عنصراً. الهدف هو ترقيم سريع ومتوقع يبقى مستقراً مع نمو البيانات، مع قواعد تعمل بنفس الطريقة للجداول والقوائم اللانهائية.
أساسيات الترقيم التي تعتمد عليها واجهة المستخدم
الترقيم هو تقسيم قائمة طويلة إلى قطع أصغر حتى يستطيع الشاشة تحميلها ورسمها بسرعة. بدلاً من طلب كل سجل من الواجهة الخلفية، يطلب العميل الشريحة التالية من النتائج.
أهم تحكم هو حجم الصفحة (المسمّى غالباً limit). الصفحات الأصغر عادة ما تبدو أسرع لأن الخادم يعمل أقل والتطبيق يرسم صفوفاً أقل. لكن الصفحات الصغيرة جداً قد تبدو متقطعة لأن المستخدمين يجب أن ينقروا أو يمرّروا أكثر. لكثير من جداول الإدارة، نطاق عملي هو من 25 إلى 100 عنصر، والهاتف عادة يفضل الطرف الأدنى.
ترتيب ثابت مهم أكثر مما تتوقع معظم الفرق. إذا كان الترتيب يمكن أن يتغير بين الطلبات، يرى المستخدمون تكرارات أو صفوف مفقودة أثناء الترقيم. عادةً ما يعني الترتيب المستقر الفرز حسب حقل أساسي (مثل created_at) بالإضافة إلى فاصل (مثل id). هذا مهم سواء استخدمت ترقيم الإزاحة أو ترقيم المؤشر.
من منظور العميل، يجب أن تتضمن الاستجابة المرقمة العناصر، تلميح الصفحة التالية (رقم صفحة أو رمز مؤشر)، وفقط العدّ الذي تحتاجه الواجهة فعلاً. بعض الشاشات تحتاج إجمالي دقيق لـ “1-50 من 12,340”. الأخرى تحتاج فقط has_more.
ترقيم الإزاحة: كيف يعمل وأين يسبب المشكلات
ترقيم الإزاحة هو نهج الصفحة N التقليدي. يطلب العميل عدداً ثابتاً من الصفوف ويخبر الواجهة كم صف يجب تخطيها أولاً. ستراه كـ limit وoffset، أو كـ page وpageSize التي يحوّلها الخادم إلى إزاحة.
طلب نموذجي يبدو هكذا:
GET /tickets?limit=50&offset=950- “أعطني 50 تذكرة، مع تخطي أول 950.”
يناسب احتياجات الإدارة الشائعة: القفز إلى الصفحة 20، مسح السجلات الأقدم، أو تصدير قائمة كبيرة على دفعات. كما أنه سهل الشرح داخلياً: “انظر الصفحة 3 وستراها.”
تظهر المشكلة في الصفحات العميقة. لا تزال العديد من قواعد البيانات تضطر للمرور عبر الصفوف المتخطاة قبل إرجاع صفحتك، خاصة عندما لا يكون ترتيب الفرز مدعوماً بفهرس ضيق. قد تكون الصفحة 1 سريعة، لكن الصفحة 200 تصبح أبطأ بشكل ملحوظ، وهذا بالضبط ما يجعل شاشات الإدارة تبدو بطيئة عندما يقوم المستخدمون بالتمرير أو القفز.
المشكلة الأخرى هي التناسق عندما تتغير البيانات. تخيل أن مدير الدعم يفتح الصفحة 5 من التذاكر مرتبة بترتيب الأحدث أولاً. بينما هم ينظرون، تصل تذاكر جديدة أو تُحذف تذاكر أقدم. الإدخالات يمكن أن تدفع العناصر للأمام (تكرارات عبر الصفحات). الحذف يمكن أن يدفع العناصر للخلف (تختفي سجلات من مسار تصفح المستخدم).
لا يزال ترقيم الإزاحة مناسباً للجداول الصغيرة، مجموعات البيانات المستقرة، أو عمليات التصدير لمرة واحدة. في الجداول الكبيرة والنشطة، تظهر الحالات الحدودية بسرعة.
ترقيم المؤشر: كيف يعمل ولماذا يبقى ثابتاً
ترقيم المؤشر يستخدم مؤشرًا كمرجعية. بدلاً من القول “أعطني الصفحة 7”، يقول العميل “تابع بعد هذا العنصر بالضبط.” عادةً يشفر المؤشر قيم فرز آخر عنصر (على سبيل المثال، created_at وid) حتى يمكن للخادم المتابعة من الموضع الصحيح.
الطلب عادةً يكون فقط:
limit: كم عنصراً تُرجعcursor: رمز غير قابل للقراءة من الاستجابة السابقة (غالباً يسمىafter)
تُعيد الاستجابة العناصر بالإضافة إلى مؤشر جديد يشير إلى نهاية تلك الشريحة. الفرق العملي هو أن المؤشرات لا تطلب من قاعدة البيانات عدّ الصفوف وتخطيها. تطلب منها البدء من موضع معروف.
لهذا يبقى ترقيم المؤشر سريعاً لقوائم التمرير الأمامي. مع فهرس جيد، تستطيع قاعدة البيانات القفز إلى “العناصر بعد X” ثم قراءة الـ limit التالي من الصفوف. مع الإزاحات، غالباً ما يضطر الخادم إلى مسح (أو على الأقل تخطي) عدد متزايد من الصفوف مع نمو الإزاحة.
من منظور سلوك الواجهة، يجعل ترقيم المؤشر زر “التالي” طبيعياً: تأخذ المؤشر المعاد وتعيده في الطلب التالي. “السابق” اختياري وأكثر تعقيداً. بعض واجهات برمجة التطبيقات تدعم مؤشر before، بينما يقوم البعض الآخر بجلب بالعكس ثم عكس النتائج.
متى تختار المؤشر، الإزاحة، أو مزيج
يبدأ الاختيار بكيفية استخدام الناس للقائمة فعلياً.
ترقيم المؤشر يناسب أفضل عندما يتحرك المستخدمون غالباً إلى الأمام وتكون السرعة هي الأهم: سجلات النشاط، الدردشات، الطلبات، التذاكر، ومسارات التدقيق، ومعظم التمرير اللانهائي على الهواتف. كما يتصرف بشكل أفضل عندما تُدرج أو تُحذف صفوف جديدة أثناء تصفح شخص ما.
ترقيم الإزاحة منطقي عندما يقفز المستخدمون كثيراً: جداول الإدارة الكلاسيكية بأرقام صفحات، انتقل إلى صفحة، والتنقل السريع ذهاباً وإياباً. هو سهل الشرح، لكنه قد يبطئ على مجموعات كبيرة وأقل استقراراً عندما تتغير البيانات تحته.
طريقة عملية للقرار:
- اختر المؤشر عندما يكون الفعل الرئيسي هو “التالي، التالي، التالي.”
- اختر الإزاحة عندما يكون “الانتقال إلى الصفحة N” مطلباً حقيقياً.
- تعامل مع الإجماليات كخيار. الحسابات الدقيقة قد تكون مكلفة على الجداول الضخمة.
الأنظمة المختلطة شائعة. نهج واحد هو استخدام المؤشر للـ next/prev للسرعة، مع وضع قفزة صفحة اختياري لمجموعة فرعية صغيرة ومفلترة حيث تبقى الإزاحات سريعة. نهج آخر هو استرجاع بالمؤشر مع أرقام صفحات مبنية على لقطة مؤقتة، حتى يشعر الجدول بالألفة دون تحويل كل طلب إلى عمل ثقيل.
عقدة واجهة برمجة تطبيقات متسقة تعمل على الويب والهاتف
تشعر واجهات الإدارة بأنها أسرع عندما تتصرف كل نقطة نهاية للقائمة بنفس الطريقة. يمكن لواجهة المستخدم أن تتغير (جدول ويب بأرقام صفحات، قائمة هاتف لانهائية)، لكن يجب أن تبقى عقدة الواجهة ثابتة حتى لا تُعيد تعلم قواعد الترقيم لكل شاشة.
عقدة عملية لها ثلاثة أجزاء: الصفوف، حالة التصفح، والإجماليات الاختيارية. احتفظ بالأسماء متطابقة عبر نقاط النهاية (tickets، users، orders)، حتى لو اختلف وضع الترقيم الداخلي.
إليك شكل استجابة يعمل جيداً لكل من الويب والهاتف:
{
"data": [ { "id": "...", "createdAt": "..." } ],
"page": {
"mode": "cursor",
"limit": 50,
"nextCursor": "...",
"prevCursor": null,
"hasNext": true,
"hasPrev": false
},
"totals": {
"count": 12345,
"filteredCount": 120
}
}
بضع تفاصيل تجعل هذا سهل إعادة الاستخدام:
page.modeيخبر العميل بما تفعله الخادم دون تغيير أسماء الحقول.limitدائماً هو حجم الصفحة المطلوب.nextCursorوprevCursorموجودان حتى لو كان أحدهما null.totalsاختياري. إذا كان مكلفاً، أعده فقط عندما يطلب العميل.
يمكن لجدول الويب أن يستمر في عرض “الصفحة 3” باحتفاظه بمؤشر صفحته الخاص واستدعاء الواجهة مراراً. يمكن لقائمة الهاتف تجاهل أرقام الصفحات وطلب الشريحة التالية فقط.
إذا كنت تبني كل من واجهات الويب والهاتف في AppMaster (appmaster.io)، تعود عليك عقدة ثابتة كهذه بسرعة. يمكن إعادة استخدام نفس سلوك القائمة عبر الشاشات دون منطق ترقيم مخصص لكل نقطة نهاية.
قواعد الفرز التي تحافظ على ثبات الترقيم
الفرز هو المكان الذي ينهار عنده الترقيم عادةً. إذا كان الترتيب يمكن أن يتغير بين الطلبات، يرى المستخدمون تكرارات، فجوات، أو صفوف «مفقودة».
اجعل الفرز عقدة، وليس اقتراحاً. انشر الحقول والاتجاهات المسموح بها، ورفض أي شيء آخر. هذا يحافظ على واجهة برمجة التطبيقات متوقعة ويمنع العملاء من طلب فرز بطيء يبدو بريئاً في التطوير.
الفرز المستقر يحتاج فاصل فريد. إذا قمت بالفرز حسب created_at وشارك سجلان نفس الطابع الزمني، أضف id (أو عمود فريد آخر) كمفتاح فاصل أخير. بدون ذلك، تترك قاعدة البيانات الحرية لإرجاع القيم المتساوية بأي ترتيب.
قواعد عملية تصمد:
- اسمح بالفرز فقط على حقول مفهرسة ومحددة جيداً (مثلاً
created_at,updated_at,status,priority). - دائماً أضف فاصل فريد كمفتاح نهائي (مثلاً
id ASC). - عرّف فرزاً افتراضياً (مثلاً
created_at DESC, id DESC) وابقه ثابتاً عبر العملاء. - وثّق كيفية ترتيب القيم الخالية (مثلاً "nulls last" للتواريخ والأرقام).
الفرز يؤثر أيضاً في توليد المؤشر. يجب أن يشفر المؤشر قيم فرز آخر عنصر بالترتيب، بما في ذلك الفاصل، حتى يتمكن الطلب التالي من الاستعلام عن "بعد" ذلك الزوج. إذا تغيّر الفرز، تصبح المؤشرات القديمة غير صالحة. اعتبر معلمات الفرز جزءاً من عقدة المؤشر.
الفلاتر والإجماليات بدون كسر العقد
يجب أن تشعر الفلاتر منفصلة عن الترقيم. تقول الواجهة: "أرني مجموعة صفوف مختلفة"، ثم تسأل "رقم خلال تلك المجموعة". إذا خلطت حقول الفلتر مع رمز الترقيم أو تعاملت مع الفلاتر كخيار غير مُتحقق، تحصل على سلوك يصعب تتبعه: صفحات فارغة، تكرارات، أو مؤشر يشير فجأة إلى مجموعة بيانات مختلفة.
قاعدة بسيطة: الفلاتر في معلمات الاستعلام العادية (أو جسم طلب POST)، والمؤشر غامض ولا يكون صالحاً إلا لتلك المجموعة الدقيقة من الفلاتر والفرز. إذا غيّر المستخدم أي فلتر (status، نطاق تاريخ، assignee)، يجب على العميل إسقاط المؤشر القديم والبدء من البداية.
كن صارماً بشأن الفلاتر المسموح بها. يحمي الأداء ويحافظ على سلوك متوقع:
- ارفض حقول فلتر غير معروفة (لا تتجاهلها بصمت).
- تحقق من الأنواع والنطاقات (تواريخ، قوائم، معرفات).
- حدد حدًا للفلاتر الواسعة (مثلاً، حد أقصى 50 معرّف في قائمة IN).
- طبّق نفس الفلاتر على البيانات والإجماليات (لا أرقام غير متطابقة).
الإجماليات هي المكان الذي يتباطأ فيه كثير من واجهات البرمجة. الأعداد الدقيقة يمكن أن تكون مكلفة على الجداول الكبيرة، خاصة مع فلاتر متعددة. لديك عموماً ثلاثة خيارات: دقيق، تقريبي، أو لا شيء. الدقيق رائع للمجموعات الصغيرة أو عندما يحتاج المستخدمون فعلاً لعرض "1-25 من 12,431". التقريبي غالباً ما يكون كافياً لشاشات الإدارة. لا شيء مقبول عندما تحتاج فقط لخاصية "تحميل المزيد".
لتجنب إبطاء كل طلب، اجعل الإجماليات اختيارية: احسبها فقط عندما يطلب العميل (مثلاً بعلم مثل includeTotal=true)، خزّنها مؤقتاً لفترة قصيرة لكل مجموعة فلتر، أو أعد الإجماليات فقط على الصفحة الأولى.
خطوة بخطوة: صمم ونفّذ نقطة النهاية
ابدأ بالإعدادات الافتراضية. نقطة نهاية قائمة تحتاج ترتيباً ثابتاً، بالإضافة إلى فاصل للصفوف التي تشترك في نفس القيمة. مثلاً: createdAt DESC, id DESC. الفاصل (id) هو ما يمنع التكرارات والفجوات عند إضافة سجلات جديدة.
عّرِف شكل طلب واحد وابقه بسيطاً. المعلمات النموذجية هي limit, cursor (أو offset), sort, وfilters. إذا دعمت الوضعين، اجعلهما متبادلَيْن: إما يرسل العميل cursor، أو يرسل offset، ولكن لا كلاهما.
حافظ على عقدة استجابة متسقة حتى يتمكن واجهات الويب والهاتف من مشاركة نفس منطق القائمة:
items: صفحة السجلاتnextCursor: المؤشر لجلب الصفحة التالية (أوnull)hasMore: قيمة منطقية حتى تقرر الواجهة إظهار "تحميل المزيد"total: إجمالي السجلات المطابقة (nullما لم يطلب إن كان العد مكلفاً)
تختلف طرق التنفيذ حيث يتباين النهجان.
استعلامات الإزاحة عادةً: ORDER BY ... LIMIT ... OFFSET ...، والتي يمكن أن تبطئ على الجداول الكبيرة.
استعلامات المؤشر تستخدم شروط البحث بناءً على آخر عنصر: "أعطني عناصر حيث (createdAt, id) أصغر من (createdAt, id) الأخير". هذا يحافظ على أداء أكثر استقراراً لأن قاعدة البيانات يمكنها استخدام الفهارس.
قبل الشحن، أضف ضوابط حماية:
- حد أقصى للـ
limit(مثلاً، 100 كحد أعلى) وضع افتراضي. - تحقق من
sortمقابل قائمة مسموح بها. - تحقق من الفلاتر حسب النوع ورفض المفاتيح غير المعروفة.
- اجعل
cursorغامضاً (شفر قيم الفرز الأخيرة) ورفض المؤشرات المشوهة. - قرر كيف يطلب
total.
اختبر مع تغير البيانات تحتك. أنشئ وحذف سجلات بين الطلبات، حدّث حقول تؤثر على الفرز، وتحقق ألا ترى تكرارات أو صفوف مفقودة.
مثال: قائمة تذاكر تبقى سريعة على الويب والهاتف
يفتح فريق الدعم شاشة إدارة لمراجعة أحدث التذاكر. يحتاجون أن تبدو القائمة فورية، حتى مع وصول تذاكر جديدة وتحديث الوكلاء للتذاكر القديمة.
على الويب، الواجهة هي جدول. الفرز الافتراضي حسب updated_at (الأحدث أولاً)، والفريق غالباً ما يفلتر على Open أو Pending. يمكن لنفس نقطة النهاية دعم كلا الحالتين بفرز مستقر ورمز مؤشر.
GET /tickets?status=open&sort=-updated_at&limit=50&cursor=eyJ1cGRhdGVkX2F0IjoiMjAyNi0wMS0yNVQxMTo0NTo0MloiLCJpZCI6IjE2OTMifQ==
تظل الاستجابة متوقعة للواجهة:
{
"items": [{"id": 1693, "subject": "Login issue", "status": "open", "updated_at": "2026-01-25T11:45:42Z"}],
"page": {"next_cursor": "...", "has_more": true},
"meta": {"total": 128}
}
على الهاتف، تغذي نفس نقطة النهاية التمرير اللانهائي. يحمل التطبيق 20 تذكرة في كل مرة، ثم يرسل next_cursor لجلب الدفعة التالية. لا منطق رقم صفحة، ومفاجآت أقل عندما تتغير السجلات.
المفتاح أن المؤشر يشفر موضع آخر عنصر مرئي (مثلاً، updated_at بالإضافة إلى id كفاصل). إذا تم تحديث تذكرة أثناء تمرير الوكيل، قد تنتقل نحو الأعلى عند التحديث التالي، لكنها لن تسبب تكرارات أو فجوات في الخلاصة التي تم تمريرها بالفعل.
الإجماليات مفيدة، لكنها مكلفة على المجموعات الكبيرة. قاعدة بسيطة هي إرجاع meta.total فقط عندما يطبق المستخدم فلتر (مثل status=open) أو يطلبه صراحة.
أخطاء شائعة تسبب التكرارات والفجوات والتأخير
معظم أخطاء الترقيم ليست في قاعدة البيانات. تأتي من قرارات واجهة برمجة تطبيقات صغيرة تبدو جيدة في الاختبار، ثم تنهار عندما تتغير البيانات بين الطلبات.
أكثر سبب شائع للتكرارات (أو السجلات المفقودة) هو الفرز على حقل ليس فريداً. إذا فرزت حسب created_at وشاركت سجلان نفس الطابع الزمني، قد يتبدل الترتيب بين الطلبات. الحل بسيط: أضف دائماً فاصلاً مستقراً، عادة المفتاح الأساسي، وعامل الفرز كزوج مثل (created_at desc, id desc).
مشكلة شائعة أخرى هي السماح للعملاء بطلب أي حجم صفحة. طلب كبير واحد يمكن أن يرفع استهلاك CPU والذاكرة وزمن الاستجابة، مما يبطئ كل شاشة إدارة. اختر افتراضاً معقولاً وحد أقصى صارم، وأعد خطأً عندما يطلب العميل أكثر.
الإجماليات أيضاً قد تؤذي. عد كل الصفوف المطابقة في كل طلب يمكن أن يصبح أبطأ جزء في نقطة النهاية، خاصة مع الفلاتر. إذا كانت الواجهة تحتاج الإجماليات، اجلبها فقط عند الطلب (أو أعد تقريباً)، وتجنب حظر التمرير على العد الكامل.
الأخطاء التي غالباً ما تخلق فجوات وتكرارات وتباطؤ:
- الفرز بدون فاصل فريد (ترتيب غير مستقر)
- أحجام صفحات غير محدودة (إرهاق الخادم)
- إرجاع الإجماليات في كل مرة (استعلامات بطيئة)
- خلط قواعد الإزاحة والمؤشر في نفس النقطة (سلوك عميل محير)
- إعادة استخدام نفس المؤشر عند تغيّر الفلاتر أو الفرز (نتائج خاطئة)
أعد تعيين الترقيم كلما تغيّر الفلتر أو الفرز. اعتبر الفلتر الجديد بحثاً جديداً: امسح المؤشر/الإزاحة وابدأ من الصفحة الأولى.
قائمة فحص سريعة قبل الإطلاق
شغّل هذا مرة واحدة مع واجهة API وواجهة المستخدم جنباً إلى جنب. معظم المشاكل تحدث في العقدة بين شاشة القائمة والخادم.
- الفرز الافتراضي مستقر ويتضمن فاصل فريد (مثلاً
created_at DESC, id DESC). - حقول واتجاهات الفرز ضمن قائمة مسموح بها.
- يطبق حد أقصى لحجم الصفحة، مع قيمة افتراضية منطقية.
- رموز المؤشر غامضة، والمؤشرات غير الصالحة تفشل بطريقة متوقعة.
- أي تغيير فلتر أو فرز يعيد تعيين حالة الترقيم.
- سلوك الإجماليات صريح: دقيق، تقريب أو محذوف.
- نفس العقدة تدعم الجدول والتمرير اللانهائي بدون حالات خاصة.
الخطوات التالية: قيّم قوائمك واجعلها متسقة
اختر قائمة إدارة واحدة يستخدمها الناس يومياً واجعلها معيارك الذهبي. نقطة نهاية نشطة مثل Tickets أو Orders أو Users بداية جيدة. بمجرد أن تصبح تلك النقطة سريعة ومتوقعة، انسخ نفس العقدة عبر بقية شاشات الإدارة.
اكتب العقدة حتى لو كانت قصيرة. كن صريحاً بشأن ما تقبله الواجهة وما تُرجعه حتى لا يخمن فريق الواجهة ويختلق قواعد مختلفة لكل نقطة نهاية.
معيار بسيط لتطبيقه على كل نقطة نهاية للقائمة:
- الفرز المسموح: أسماء حقول دقيقة، الاتجاه، وإفتراضي واضح (ومع فاصل مثل
id). - الفلاتر المسموحة: الحقول التي يمكن فلترتها، صيغ القيم، وماذا يحدث عند فلاتر غير صالحة.
- سلوك الإجماليات: متى تعيد عدّاً، متى تعيد "غير معروف"، ومتى تتخطاه.
- شكل الاستجابة: مفاتيح متسقة (
items, معلومات التصفح، الفرز/الفلاتر المطبقة، الإجماليات). - قواعد الأخطاء: رموز حالة متسقة ورسائل تحقق قابلة للقراءة.
إذا كنت تبني هذه الشاشات الإدارية باستخدام AppMaster (appmaster.io)، يساعد توحيد عقدة الترقيم مبكراً. يمكنك إعادة استخدام نفس سلوك القائمة عبر تطبيق الويب وتطبيقات الهاتف الأصلية، وتقلّص الوقت المهدور في مطاردة حالات حواف الترقيم لاحقاً.
الأسئلة الشائعة
يستخدم ترقيم الإزاحة limit مع offset (أو page/pageSize) لتخطي صفوف، لذا الصفحات العميقة غالباً ما تصبح أبطأ لأن قاعدة البيانات تضطر للمرور بعدد أكبر من السجلات. يستخدم ترقيم المؤشر رمز after معتمد على قيم الفرز لآخر عنصر، فيستطيع القفز إلى موضع معروف ويظل سريعاً أثناء التصفح الأمامي.
لأن الصفحة الأولى عادةً سهلة وسريعة، أما الصفحة 200 فتجبر قاعدة البيانات على تخطي عدد كبير من الصفوف قبل أن تعيد أي شيء. مع الفرز والتصفية، يزداد العمل، لذلك كل نقرة تبدأ تبدو كاستعلام ثقيل جديد بدلاً من جلب سريع.
استخدم دائماً فرزاً مستقراً مع فاصل فريد، مثل created_at DESC, id DESC أو updated_at DESC, id DESC. من دون فاصل فريد، السجلات التي تشترك في نفس الطابع الزمني قد تتبدل ترتيباتها بين الطلبات، وهذا سبب شائع للتكرارات والصفوف «المفقودة».
فضل ترقيم المؤشر للقوائم التي يتحرك المستخدمون فيها للأمام غالباً ويهتمون بالسرعة، مثل سجلات النشاط، التذاكر، الطلبات، والتمرير اللانهائي على الهاتف المحمول. يظل الترقيم بالمؤشر متسقاً عند إضافة أو حذف صفوف لأن المؤشر يربط الصفحة التالية بموقع آخر عنصر محدد.
ترقيم الإزاحة مناسب عندما تكون ميزة «الانتقال إلى الصفحة N» مطلوبة فعلاً، والمستخدمون يقفزون كثيراً بين الصفحات. هو أيضاً مريح للجداول الصغيرة أو المجموعات الثابتة حيث لا يهم تباطؤ الصفحات العميقة أو تحوّل النتائج.
وحّد شكل الاستجابة عبر نقاط النهاية وضمّن العناصر، حالة التصفّح، وإجماليات اختيارية. افتراضي عملي هو items، كائن page (مع limit, nextCursor/prevCursor أو offset) وعلم بسيط مثل hasNext حتى يمكن لكل من جداول الويب وقوائم الهاتف إعادة استخدام نفس منطق العميل.
لأن COUNT(*) الدقيق على مجموعات كبيرة ومفلترة قد يصبح أبطأ جزء في الطلب ويجعل كل تغيير صفحة يبدو متأخراً. افتراض آمن هو جعل الإجماليات اختيارية، إرجاعها فقط عند الطلب، أو إرجاع has_more عندما يحتاج الواجهة فقط لخاصية "تحميل المزيد".
اعتبر الفلاتر جزءًا من مجموعة البيانات، واعتبر المؤشر صالحاً فقط لذلك التركيب المحدد من الفلتر والفرز. إذا غيّر المستخدم أي فلتر أو فرز، أعد تعيين الترقيم وابدأ من الصفحة الأولى؛ إعادة استخدام مؤشر قديم بعد تغيّر الفلاتر طريقة شائعة للحصول على صفحات فارغة أو نتائج مربكة.
اعتمد قائمة سماح لحقول الفرز والاتجاهات ورفض أي شيء آخر حتى لا يطلب العملاء ترتيباً بطيئاً أو غير مستقر عن طريق الخطأ. فضّل الفرز على حقول مفهرسة دائماً وألحِق فاصلًا فريداً مثل id ليكون الترتيب حتميّاً بين الطلبات.
فرض حد أقصى على limit، تحقق من الفلاتر ومعاملات الفرز، واجعل رموز المؤشر غير قابلة للقراءة ومتحقّق منها بدقة. إذا كنت تبني شاشات الإدارة في AppMaster، الحفاظ على هذه القواعد موحّدة عبر جميع نقاط النهاية يجعل إعادة استخدام السلوك أسهل دون إصلاحات ترقيم خاصة بكل شاشة.


