使用 vuex 时对 Firestore 数据进行分页并将新数据附加到状态 [英] Paginating firestore data when using vuex and appending new data to the state

查看:25
本文介绍了使用 vuex 时对 Firestore 数据进行分页并将新数据附加到状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实现了以下内容来显示分页查询(这是 Tony O'Hagan 在这篇文章中提出的建议:如何从 VueFire 查询中获取最后一个文档):

I implemented the following to display a paginated query (this was suggested by Tony O'Hagan in this post: 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 个文档集.我该怎么做?

When the user scrolls to the end of the page, I call bindMoreUsers which updates the state.users to the next set of 8 documents. I need to be able to append to the state.users as opposed to overwrite the original set of 8 documents. How can I do this?

推荐答案

坦白:我还没有在我当前的应用程序上实现分页,但我会这样做.

Confession: I've not yet implemented pagination on my current app but here's how I'd approach it.

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

In my previous answer I explained how to keep references to the Firestore doc objects inside each element of the state array that is bound by VuexFire or VueFire. In Solution #1 below we use these doc objects to implement Firestore's recommended cursor based pagination of a query result sets using startAfter(doc) query condition instead of the slower more expensive offset clause.

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

Keep in mind that since we're using Vuexfire/Vuefire we're saying that we wish to subscribe to live changes to our query so our bound query will define precisely what ends up in our bound array.

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

Solution #1. Paging forward/backward loads and displays a horizontal slice of the full dataset (our bound array maintains the same size = page size). This is not what you requested but might be a preferred solution given the Cons of other solutions.

  • 优点:服务器:对于大型数据集,此分页查询将以最少的成本和延迟执行.
  • 优点:客户端:内存占用少,渲染速度最快.
  • 缺点:分页可能不像滚动.用户界面可能只有向前/向后的按钮.
  • 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).
  • Pros: Server: For large datasets, this pagination query will execute with least cost and delay.
  • Pros: Client: Maintains a small in memory footprint and will render fastest.
  • Cons: Pagination will likely not feel like scrolling. UI will likely just have buttons to go fwd/backward.
  • Page Forward: Get the doc object from the last element of our state array and apply a startAfter(doc) condition to our updated view query that binds our array to the next page.
  • Page Backward: Bit Harder! Get the doc object from the first element of our bound state array. Run our page query with startAfter(doc), limit (1), offset(pagesize-1) and reverse sort order. The result is the starting doc (pageDoc) of the previous page. Now use startAfter(pageDoc) and forward sort order and limit(pageSize) to rebind the state array (same query as Page Forward but with doc = pageDoc).

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

NOTE: In the general case, I'd argue that we can't just keep the pageDoc values from previous pages (to avoid our reverse query) since we're treating this as a 'live' update filtered list so the number of items still remaining from previous pages could have radically changed since we scrolled down. Your specific application might not expect this rate of change so perhaps keeping past pageDoc values would be smarter.

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

Solution #2. Paging forward, extends the size of the query result and bound array.

  • 优点:随着数组的增长,用户体验感觉就像正常滚动一样.

  • Pros: UX feels like normal scrolling since our array grows.

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

Pros: Don't need to use serializer trick since we're not using startAfter() or endBefore()

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

Cons: Server: You're reloading from Firestore the entire array up to the new page every time you rebind to a new page and then getting live updates for growing array. All those doc reads could get pricey!

缺点:客户端:向前翻页时渲染可能会变慢 - 尽管 shadow DOM 可以解决此问题.每次重新加载时 UI 可能会闪烁,因此需要更多 UI 魔术(延迟渲染,直到数组完全更新).

Cons: Client: Rendering may get slower as you page forward - though shadow DOM may fix this. UI might flicker as you reload each time so more UI magic tricks needed (delay rendering until array is fully updated).

优点:如果我们使用 无限滚动 功能.我得测试一下.

Pros: Might work well if we're using an infinite scrolling feature. I'd have to test it.

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

Page Forward: Add pageSize to our query limit and rebind - which will re-query Firestore and reload everything.

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

Page Backward: Subtract pageSize from our query limit and rebind/reload (or not!). May also need to update our scroll position.

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

Solution #3. Hybrid of Solution #1 and #2. We could elect to use live Vuexfire/Vuefire binding for just a slice of our query/collection (like solution #1) and use a computed function to concat it with an array containing the pages of data we've already loaded.

  • 优点:降低 Firestore 查询成本和查询延迟,但现在具有平滑滚动的外观和感觉,因此可以使用无限滚动 UI.给我一个 Koolaid!
  • 缺点:我们必须尝试跟踪显示数组的哪一部分,并使该部分绑定并实时更新.
  • Page Forward/Backward:与绑定当前数据页的解决方案 #1 相同,除了我们现在必须将前一页数据复制到我们的非活动数据数组中,并将一个小的计算函数编码到 concat() 两个数组,然后将 UI 列表绑定到这个计算数组.
  • Pros: Reduces the Firestore query cost and query delay but now with a smooth scrolling look and feel so can use Infinite scrolling UI. Hand me a Koolaid!
  • Cons: We'll have to try to keep track of which part of our array is displayed and make that part bound and so live updated.
  • Page Forward/Backward: Same deal as Solution #1 for binding the current page of data, except we now have to copy the previous page of data into our non-live array of data and code a small computed function to concat() the two arrays and then bind the UI list to this computed array.

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

Solution #3a We can cheat and not actually keep the invisible earlier pages of data. Instead we just replace each page with a div (or similar) of the same height ;) so our scrolling looks we've scrolled down the same distance. As we scroll back we'll need to remove our sneaky previous page div and replace it with the newly bound data. If you're using infinite scrolling, to make the scrolling UX nice and smooth you will need to preload an additional page ahead or behind so it's already loaded well before you scroll to the page break. Some infinite scroll APIs don't support this.

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

Solution #1 & #3 probably needs a Cookbook PR to VueFire or a nice MIT'd / NPM library. Any takers?

这篇关于使用 vuex 时对 Firestore 数据进行分页并将新数据附加到状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆