两种策略:基于偏移量(offset-based)(limit/offset)和基于游标(cursor-based)。GraphQL 没有内置分页,但社区标准是 Relay Connection 规范,它把游标分页正式化了。
两种策略:基于偏移量(offset-based)(limit/offset)和基于游标(cursor-based)。GraphQL 没有内置分页,但社区标准是 Relay Connection 规范,它把游标分页正式化了。
items(limit: 10, offset: 20))简单,但在实时数据上会出问题:如果用户翻页时插入了一行,条目会移位,你会看到重复或遗漏。它在深偏移时也会变慢(数据库仍要扫描被跳过的行)。一个 connection 把列表包裹在 edges(每个都带一个 cursor 和 node)外加 pageInfo 中:
type Query {
posts(first: Int!, after: String): PostConnection! # first = 每页大小,after = 游标
}
type PostConnection {
edges: [PostEdge!]!
pageInfo: PageInfo!
}
type PostEdge {
cursor: String! # 表示"这个 node"位置的不透明 token
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 },
};
游标是不透明的——通常是排序键的 base64——所以客户端把它当作 token,而非数字。
选 offset 还是 cursor,体现你是否理解并发写入下的一致性和深页性能。了解 Relay connection 的结构很重要,因为 Apollo/Relay 客户端对它有内置的缓存处理。
一个包含详细解答的 IT 面试题库——从初级到高级。
捐赠