分页
记录开发duozhuavue💚主页的分页功能时的实践。
关于分页
分页的样子
有编号的分页
无编号,点击加载
无编号,滚动加载
跳转到下一页时要做什么
获取数据
1
2//根据传入的参数获取新数据
const incoming = getNextPage(...);更新缓存
1
2
3
4
5
6
7// 每个字段都可以有自己的 merge() 函数用于配置缓存合并策略
function merge(existing, incoming) {
...
existing = [...existing, ...incoming];
...
return existing;
}
想要的功能
在已经有缓存的情况下,分页读取、显示(paginated read)缓存数据。
尝试
基本配置
graphql
查询设置
1 | // first: 获取几个分类 |
关于 categoryFeed
中的 pageInfo
和 edges
,可以参考 GraphQL Cursor Connections Specification
设置缓存合并策略
1 | Query: { |
缓存的合并策略,可以参考 Customizing the behavior of cached fields
relayStylePagination
可以参考 Relay-style cursor pagination
使用 fetchMore()
查询设置
1 | const after = ref('') // 从哪里开始获取数据 |
首次使用
页面加载
请求第一条数据,缓存为空,于是请求服务器,获得第一条数据,写入缓存,返回给页面
点击加载更多分类
1
2
3
4
5
6
7
8
9
10
11
12const loadMoreCategories = function () {
fetchMore({
variables: {
after: cursor.value // 这里的cursor是第一页数据的 pageInfo 中的信息
}
}).then(({ data: { categoryFeed } }) => {
after.value = categoryFeed.pageInfo.endCursor
console.log(
'$after is updated, there will be a new categoryFeed query...'
)
})
}调用
loadMoreCategories()
,执行带有新参数的fetchMore()
新数据会依照合并策略写入缓存
👀 为了读取更新后的缓存,需要在
then()
中更新after
的值(更新会触发查询执行,该after
参数可以用于请求缓存中特定部分的数据再次点击加载更多分类
刷新页面后
- 页面加载
- 点击加载更多分类
- 再次点击加载更多分类
效果及问题
看起来没什么问题!但是问题发生在有缓存时的第一次点击之后
缓存中的分类如今有三条数据
刷新页面,页面加载,ok!
点击加载更多分类,ops!缓存中的分类变成两条了!!
问题分析
在有缓存的情况下,我的期望是从缓存读取数据。但是实际情况是,每次调用 fetchMore()
都会请求服务器数据。
看了一下 fetchMore()
的源码,它是这么定义的
解决方法
- 不用
fetchMore()
- 重新定义缓存合并策略,如果新结果已经被缓存,就不执行
merge()
函数(...relayStylePagination()
中有默认的merge()
函数
使用 useQuery
为了不使用 fetchMore()
,刚开始我使用了 useLazyQuery()
, 它会返回一个需要主动调用获取数据的 load()
函数。我在页面挂载后,调用 load()
获取第一页数据,在点击发生时再次调用 load()
并传入相应的参数,获取新数据。这种实现满足了一些需求,但是也有它的问题。于是我又去考虑别的策略,很快就意识到,相同的逻辑其实利用 useQuery()
也可以实现,尽管它们存在相同的问题。
给查询传入响应式变量,想要获取下一页数据时,只需要更改变量的值(查询会自动更新
查询设置
1 | const after = ref('') // 从哪里开始获取数据 |
首次使用
页面加载
请求第一条数据,缓存为空,于是请求服务器,获取第一条数据,写入缓存,返回到页面
点击加载更多分类
1
2
3const loadMoreCategories = function () {
after.value = cursor.value // 只需要更新 after 的值
}调用
loadMoreCategories()
查询再次执行,缓存未命中,请求服务器,得到第二条数据,写入缓存,返回到页面
刷新页面
页面加载
after
参数为""
请求第一条数据,缓存命中,返回到页面
点击加载更多分类
调用
loadMoreCategories()
查询再次执行,命中缓存,得到两条数据,返回到页面
1
2
3
4// 自定义缓存的 read() 函数
res.edges = [
...existing.edges.slice(0, startIndex + first); // 总是从头开始读取数据
];
问题
🤨 在没有缓存的情况下,请求新数据时,该组件会整体刷新
因为 categoryFeed
查询确实是重新执行了一次,所以整体刷新是正常现象。这种正常现象不是我要的效果。
重新定义缓存合并策略
- TODO
trade-off
relayStylePagination()
的默认设置中,对缓存的读取是全部读取。也就是说,当你从别的页面回到主页,你可以看到之前得到的所有数据,并没有分页地读取缓存数据。
我也在想,在主页这样滚动浏览的情境下,分页读取到底有没有必要,想了半天,好像是没必要啊!
回到最初的起点
最终我还是决定采用使用 fetchMore()
的方法。虽然绕了一圈,但是这段时间为了解决分页问题不停探索,还是学到了很多东西。
🎏 写完duozhuavue就去面duozhuayu!