问题
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 })
})
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?
回答1:
Confession: I've not yet implemented pagination on my current app but here's how I'd approach it.
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.
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.
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.
- 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 astartAfter(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 usestartAfter(pageDoc)
and forward sort order and limit(pageSize) to rebind the state array (same query as Page Forward but with doc = 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.
Solution #2. Paging forward, extends the size of the query result and bound array.
Pros: UX feels like normal scrolling since our array grows.
Pros: Don't need to use
serializer
trick since we're not usingstartAfter()
orendBefore()
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!
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: Add
pageSize
to our query limit and rebind - which will re-query Firestore and reload everything.Page Backward: Subtract
pageSize
from our query limit and rebind/reload (or not!). May also need to update our scroll position.
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.
- 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.
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.
Solution #1 & #3 probably needs a Cookbook PR to VueFire or a nice MIT'd / NPM library. Any takers?
来源:https://stackoverflow.com/questions/62639778/paginating-firestore-data-when-using-vuex-and-appending-new-data-to-the-state