GraphQL 之前已经夸了不少了,见 REST vs GraphQL,确实在标准化方面远超 REST 了,这也是我想要切换到 GraphQL 的一大原因。不过 GraphQL 甚至连分页也提了一个标准,挺有意思的,看了看他们的 GraphQL Cursor Connections Specification ,想做一些自己理解的记录。
这样标准化的好处就是可以让前端也有一个类似的组件去处理分页,让这部分的工作就直接通过这个公共组件给覆盖了。想象一下,如果有更多的东西可以以这种方式形成标准,那相当于不少工作也就可以复用了呢?
分页这个东西虽然没有这样子如此明确的标准,但其实从各种文档、书籍、博客里也都逐渐形成了类似的标准了呢,毕竟这已经是一个非常古老的需求了。这里我就以最简单的方式介绍目前两种针对不同场景的分页风格,更多的信息可以从下文的参考中找的到。
对于记录不多并且增删不频繁的场景,limit-offset
的方式基本是最健全的分页了。通过从数据库的 select * from xx limit xxx offset xxx
配合 select count(*) from xx
可以获取非常全面的分页信息以及动作:
但缺点也很明确:offset 这个语法对数据记录多的场景不友好,查询速度会明显下降,同时对快速变化的数据也不友好,很容易丢查询数据。
cursor-limit
分页则是以类似于 select * from xxx where cursor < xxx order by cursor limit xx
的方式获取相对于 cursor
的记录。相对于 limit-offset
的方式会很难获取以下信息:
GraphQL 定义凡事以 Connection
结尾的结构都遵循 Cursor Connection
的数据结构,并且名为 PageInfo
的东西都是 Cursor Connection
下的 PageInfo
结构。
first after
与 last before
必须分组出现,也就是说可以是以下几种形式:
articles(first: Int!, after: String!)
articles(last: Int!, before: String!)
articles(first: Int, after: String, last: Int, before: String)
第三种就是同时支持正序和倒序查找,也就是支持向前翻页。
这里就按照 first after
为例子做介绍了,last before
都是反过来的,就不复读机了:
after + 1
去查询,如果返回的个数为 after + 1
那就意味着有下一页(hasNextPage),否则就是没有。hasPreviousPage
那意味着每次都多一个查询 select * from xxx where cursor < #{startCursor} limit 1
,如果有结果就意味着返回 true
。false
就好了。最近把 realworld-example-app 改写的差不多了,已经同时支持 GraphQL 和 REST 了,其中 GraphQL 部分的分页也是按照 cursor connection
的形式做的,然后我需要找个前端的 RealWorld 项目去对接下,看看我这个分页折腾的对不对了。