【问题标题】:Paginating firestore data when using vuex and appending new data to the state使用 vuex 并将新数据附加到状态时对 firestore 数据进行分页
【发布时间】:2020-10-19 17:07:56
【问题描述】:

我实现了以下内容来显示分页查询(这是 Tony O'Hagan 在这篇文章中建议的:How to get the last document from a VueFire query):

bindUsers: firestoreAction(({ bindFirestoreRef }) => {
  return bindFirestoreRef('users', 
      Firebase.firestore().collection('users').limit(8), { serialize })
}),
bindMoreUsers: firestoreAction(context => {
  return context.bindFirestoreRef('users', Firebase.firestore().collection('users').startAfter(context.state.users[context.state.users.length - 1]._doc).limit(8), { serialize })
})

当用户滚动到页面末尾时,我调用 bindMoreUsers 将 state.users 更新为下一组 8 个文档。我需要能够附加到 state.users 而不是覆盖原始的 8 个文档集。我该怎么做?

【问题讨论】:

  • 嗨@ak22 - 您需要实时更新吗?如果你是那么你可能最好的选择是使用startAt你的第一个查询的文档,而不是做一个硬编码的limit(8)做一个带有偏移量的限制,比如limit(users.length + offset)这样你保持您现有的用户列出并添加额外的offset 数量,在您的情况下为8。如果您不需要实时,那么只需列出数组并保留您的 doc.id 以保存任何 UI 编辑更新以保存回 Firestore。
  • 谢谢。我会试试这个,让你知道。
  • 所以,这基本上是绑定前 8 个文档,然后是 16 个,然后是 24 个,依此类推。那么,这不是累积了 8+16+24 次读取吗?
  • 嗨@ak22 - 它会产生额外的读取。您可以使用一些分析来帮助您决定这是否最适合您。例如,对 24 个项目进行初始读取,但只显示 8 个,然后通过跟踪用户对更多项目的消费情况,将分析与用户的行为相关联。如果有更多分页的重要用途,则权衡 UX/UI 的影响,以便最初列出更多结果以减少读取。
  • 好的,谢谢。奇怪的是,没有好的解决方案。似乎是一个常见的用例,对吧?假设您要滚动浏览成员列表。现在我想起来了,我认为不需要实时更新,因为我只显示成员名称、位置和其他一些不太可能发生太大变化的统计项目。因此,我可以直接查询而不是在商店中绑定查询。感谢您的回复。

标签: javascript vue.js vuex vuexfire


【解决方案1】:

忏悔:我还没有在我当前的应用程序上实现分页,但这是我的处理方法。

在我的previous answer 中,我解释了如何在受 VuexFire 或 VueFire 绑定的状态数组的每个元素内保留对 Firestore doc 对象的引用。在下面的解决方案 #1 中,我们使用这些 doc 对象来实现 Firestore 推荐的基于游标的查询结果集分页,使用 startAfter(doc) 查询条件而不是更慢更昂贵的 offset 子句。

请记住,由于我们使用的是 Vuexfire/Vuefire,因此我们希望订阅对查询的实时更改,因此我们的绑定查询将准确定义绑定数组中的最终结果。

解决方案 #1。向前/向后分页加载并显示整个数据集的水平切片(我们的绑定数组保持相同的大小 = 页面大小)。这不是您要求的,但考虑到其他解决方案的Cons,这可能是首选解决方案。

  • 优点:服务器:对于大型数据集,此分页查询将以最少的成本和延迟执行。
  • 优点:客户端:内存占用小,渲染速度最快。
  • 缺点:分页可能感觉不像滚动。 UI 可能只有前进/后退的按钮。
  • Page Forward:从状态数组的 last 元素中获取doc 对象,并将startAfter(doc) 条件应用于我们更新的视图查询,将我们的数组绑定到下一个页面。
  • 向后翻页:再难一点!从绑定状态数组的 first 元素中获取doc 对象。使用 startAfter(doc)、limit (1)、offset(pagesize-1) 和 reverse 排序顺序运行我们的页面查询。结果是上一页的起始文档(pageDoc)。现在使用 startAfter(pageDoc)forward 排序顺序和 limit(pageSize) 重新绑定状态数组(与 Page Forward 相同的查询,但使用 doc = pageDoc)。

注意:在一般情况下,我认为我们不能只保留以前页面的 pageDoc 值(以避免我们的反向查询),因为我们将其视为“实时”更新过滤列表,因此自从我们向下滚动以来,之前页面中剩余的项目数量可能已经发生了根本性的变化。您的特定应用程序可能不会期望这种变化率,因此保留过去的 pageDoc 值可能会更智能。

解决方案 #2。向前翻页,扩展查询结果和绑定数组的大小。

  • 优点:用户体验感觉就像正常滚动,因为我们的数组在增长。

  • 优点:不需要使用serializer 技巧,因为我们没有使用startAfter()endBefore()

  • 缺点:服务器:每次重新绑定到新页面时,您都会从 Firestore 将整个阵列重新加载到新页面,然后获取实时更新以适应不断增长的阵列。所有这些文档读取都可能变得昂贵!

  • 缺点:客户端:当您向前翻页时,渲染可能会变慢 - 尽管影子 DOM 可能会解决这个问题。每次重新加载时 UI 可能会闪烁,因此需要更多的 UI 魔术技巧(延迟渲染直到数组完全更新)。

  • 优点:如果我们使用 infinite scrolling 功能可能会很好。我得测试一下。

  • Page Forward:将 pageSize 添加到我们的查询限制并重新绑定 - 这将重新查询 Firestore 并重新加载所有内容。

  • Page Backward:从我们的查询限制中减去 pageSize 并重新绑定/重新加载(或不重新加载!)。可能还需要更新我们的滚动位置。

解决方案 #3。解决方案#1 和#2 的混合。我们可以选择对查询/集合的一部分使用实时 Vuexfire/Vuefire 绑定(如解决方案 #1),并使用计算函数将其与包含我们已经加载的数据页面的数组连接。

  • 优点:降低了 Firestore 查询成本和查询延迟,但现在具有平滑的滚动外观,因此可以使用无限滚动 UI。递给我一杯 Koolaid!
  • 缺点:我们必须尝试跟踪显示数组的哪一部分,并绑定该部分并实时更新。
  • 页面前进/后退:与解决方案#1 相同的处理方式用于绑定当前数据页,不同之处在于我们现在必须将前一页数据复制到我们的非实时数据数组中,并将一个小的计算函数编码到@987654338 @ 两个数组,然后将 UI 列表绑定到这个计算数组。

解决方案#3a 我们可以作弊,而不是真正保留不可见的早期数据页面。相反,我们只是用相同高度的div(或类似的)替换每个页面;)所以我们的滚动看起来我们已经向下滚动了相同的距离。当我们向后滚动时,我们需要删除我们偷偷摸摸的上一页div 并将其替换为新绑定的数据。如果您使用无限滚动,为了使滚动 UX 美观流畅,您需要在前面或后面预加载一个额外的页面,以便在滚动到分页符之前它已经加载好。一些无限滚动 API 不支持这一点。

解决方案 #1 和 #3 可能需要一个 Cookbook PR 到 VueFire 或一个不错的 MIT'd / NPM 库。 有接受者吗?

【讨论】:

  • @tony-ohagan,上一篇回答文章写得很好,工作也很棒。对于Solution #3a,我认为一个简单的a(锚)可以用于滚动工作,这将打开简单的滚动功能。我认为这些东西不应该存在于 VueFire 等人中。包(除非用作示例),但 NPM 也很有意义。
  • 感谢您的详细回答。我认为选项 3 在我的情况下值得追求,但这是我不清楚的部分:“我们将不得不尝试跟踪我们数组的哪个部分被显示并进行绑定并实时更新”。实际上,我有一个附带问题(可能是因为我不了解 Vuexfire 的内部原理),如果再次绑定以前绑定的文档,这是否会导致再次读取?因此,假设项目 1-10 已在视图中并且当前已绑定。然后用户滚动,现在 2-11 被绑定。往回滚动,1-10又被绑定了。第 1 项是否会导致再次读取?
  • 不会,因为每次重新绑定时都会重新查询,这会降低性能并增加成本。解决方案取决于您计划使用滚动下一个/上一个页面按钮 实现分页的人。如果您正在使用滚动,那么您正在使用像Quasar's Infinite Scroll 这样的小工具来触发下一页的加载。因此,如果 pageSize = 10,则第二页将为 11..20。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-18
  • 2019-04-02
  • 1970-01-01
  • 1970-01-01
  • 2019-11-27
  • 1970-01-01
相关资源
最近更新 更多