استراتيجيتان: قائمة على offset (limit/offset) وقائمة على cursor. لا يملك GraphQL تقسيمًا للصفحات مدمجًا، لكن المعيار المجتمعي هو مواصفة Relay Connection التي تُنظّم تقسيم الصفحات بالـ cursor.
استراتيجيتان: قائمة على offset (limit/offset) وقائمة على cursor. لا يملك GraphQL تقسيمًا للصفحات مدمجًا، لكن المعيار المجتمعي هو مواصفة Relay Connection التي تُنظّم تقسيم الصفحات بالـ cursor.
items(limit: 10, offset: 20)) بسيط لكنه ينكسر مع البيانات الحيّة: إذا أُدرج صفّ أثناء تصفح المستخدم، تنزاح العناصر فترى تكرارات أو فجوات. كما يصبح بطيئًا عند الإزاحات العميقة (فقاعدة البيانات لا تزال تمسح الصفوف المتخطاة).يلفّ الـ connection القائمة في edges (لكل منها cursor وnode) بالإضافة إلى pageInfo:
type Query {
posts(first: Int!, after: String): PostConnection! # first = حجم الصفحة، after = cursor
}
type PostConnection {
edges: [PostEdge!]!
pageInfo: PageInfo!
}
type PostEdge {
cursor: String! # رمز مبهم لموضع هذا الـ node
node: Post! # العنصر الفعلي
}
type PageInfo {
endCursor: String # مرّره مجددًا كـ 'after' للصفحة التالية
hasNextPage: Boolean! # لكي يعرف العميل متى يتوقف
}
// resolver: اجلب صفًا إضافيًا واحدًا لحساب hasNextPage بثمن زهيد
const rows = await db.posts.find({ after: args.after, limit: args.first + 1 });
const hasNextPage = rows.length > args.first;
const page = rows.slice(0, args.first);
return {
edges: page.map((p) => ({ node: p, cursor: encodeCursor(p.id) })),
pageInfo: { endCursor: page.at(-1) && encodeCursor(page.at(-1).id), hasNextPage },
};
الـ cursor مبهم — عادة base64 لمفتاح الفرز — لذا يعامله العملاء كرمز، لا كرقم.
اختيار offset مقابل cursor يُظهر أنك تفهم الاتساق تحت الكتابات المتزامنة وأداء الصفحات العميقة. معرفة شكل Relay connection مهمة لأن عملاء Apollo/Relay يملكون معالجة cache مدمجة له.
مكتبة من أسئلة مقابلات تقنية المعلومات مع إجابات مفصّلة — من المبتدئ إلى المتقدم.
تبرع