Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: custom DOM recycle #606

Closed
wants to merge 2 commits into from
Closed

perf: custom DOM recycle #606

wants to merge 2 commits into from

Conversation

ferferga
Copy link
Member

@ferferga ferferga commented Jan 19, 2021

Custom DOM recycle as discussed in #274

Features

  • Should be fully Vue 3 compatible.
  • Fully reactive: gets the difference after every items array mutation and commits the changes only to the visible items that are changed. Unchanged items remains altered, while previous implementation required the same row to be changed. Applying changes to specific items are untested though, as there was no way to test that at our current stage but now that we have search finished I need to make sure this 100% works
  • Lightweight: 4.16 MB vs 4.13 MB bundle size with my implementation.
  • All the processing is done in a single animation frame offscreen to avoid reflows.
  • Creates additional cards when the target cards prop (prop that specifies the amount of DOM elements we will always keep and swap between) are not enough to cover the viewport.
  • Only necessary variables are reactive, reducing Vue reactivity proxies as much as possible.
  • Tested in mobile.

Disadvantages

The first row is the one used for taking all the measurements of size. We have some views that mix cards with different shapes and they're not going to work well

Issues

This is already good enough to be reviewed, as I tested this a lot for weeks. However, there are still issues that does make this not a 100% perfect replacement. I already know the cause of most of them and they're easy to solve, but I want to have some feedback from the community about the whole current implementation before working on this, so, in case nobody likes it, fixing stuff doesn't end up as a completely wasted effort

  • After resizing the window while the scroll is not at the beginning of the page, some side effects might take place. Not always noticeable but it happens in pages with lots of items.

  • The process to create additional cards when the amount of targetCards is less than the amount of cards that should be visible doesn't take place after the component has been mounted, only before. To reproduce:

    • Set targetCards to 100. Zoom out your browser to 25%. Go to a library and see how additional cards are created to cover the empty space at the bottom that the zooms leaves.
    • Reload the page at 100% zoom and, after it has been mounted, zoom out at 25%: additional cards should be created but they aren't
  • Light scrolls (two key presses to the arrows in the keyboard for instance) that change direction many times in a row might lead for swaps that happens when scrolling slower to have visual artifacts.

  • When scrolling up, first row might not keep the correct order.

All fixed now

Please, test as much as you can stuff like the last one: items out of order, items swapping order in the row while scrolling, etc...

To discuss

While developing this, I had the targetCards prop set to 100. It was already much better than the current solution, but you could say cards "loading" if scrolling way too fast. Later days of testing I set it to 200 items (which I think it's still good enough for the majority of devices) to allow faster scrolling without noticing that "loading" and it's near native scrolling. What are you opinions on this value?

Comparison

All the screrenshots were taken using the following approach:

  • Build for production
  • Navigate to a library and wait 5 seconds to let the JS engine finish all the mounting tasks.
  • Scroll to the bottom and then to the top again to allow Vue to "cache" as much stuff as possible.
  • Start performance recording and start scrolling using the down arrow of the keyboard (which is a really fast scroll speed). Stop profiling after the last row was visible.

Before
before

With this implementation (targetCards set to 200)
after_200

With this implementation (targetCards set to 100)
after_100

Notice also how not only scripting time is reduced, but also memory heap

Further improvements to performance

Performance might be even better if we do the following (not for this PR though):

  • Reduce painting time (caused by the transitions of blurhashes canvas). Should be improved if we cache generated blurhashes.
  • Replace v-btn, which fills the page with mouseover events that are fired if the cursor is in the middle. Should be improved in Vuetify 3 anyway
  • We could cherrypick Various fixes and performance improvements #275 "stop rendering on fast scroll" feature.

@ferferga ferferga force-pushed the virtual-scroll branch 2 times, most recently from a637f31 to 56e3a7d Compare January 19, 2021 11:57
@codecov-io
Copy link

codecov-io commented Jan 19, 2021

Codecov Report

Merging #606 (560c5cc) into master (534de9d) will decrease coverage by 0.68%.
The diff coverage is 2.77%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #606      +/-   ##
==========================================
- Coverage   12.12%   11.43%   -0.69%     
==========================================
  Files         134      134              
  Lines        3523     3788     +265     
  Branches      523      564      +41     
==========================================
+ Hits          427      433       +6     
- Misses       3073     3332     +259     
  Partials       23       23              
Impacted Files Coverage Δ
components/Item/Card/VirtualCard.vue 0.00% <0.00%> (ø)
components/Item/ItemGrid.vue 0.00% <0.00%> (ø)
components/Layout/Images/BlurhashCanvas.vue 88.46% <100.00%> (+17.62%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 534de9d...560c5cc. Read the comment docs.

@ferferga ferferga mentioned this pull request Jan 20, 2021
16 tasks
@ferferga ferferga marked this pull request as draft January 22, 2021 22:50
@ferferga ferferga marked this pull request as ready for review January 23, 2021 10:37
@ferferga
Copy link
Member Author

@MrTimscampi @camc314 @ThibaultNocchi All the issues that the original implementation had are now fixed and I believe this is already in an state where it can 100% replace our current virtual scroller. Some obscure bugs I couldn't reproduce through this week might exist though, however, they're probably easily fixable until we release, the advantage we have of being an alpha client :).

@ferferga
Copy link
Member Author

Sonarcloud is reporting a bug because that property is still not standardized and only Chromium-based browsers use it. However, it shouldn't cause any issues in Firefox, as it's only mission is to change how painting process is laid out, visually there are no differences.

Check this for more info: #274 (comment)

targetCards: {
type: Number,
required: false,
default: 150
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested the following values: 80, 150, 200. 80 gives the best performance while having enough buffer space for light scrolling, 150 seems the sweetest spot for me and 200 gives near native scrolling appearance but, ofc, it's the most intensive of all the others.

Test your values and report back

Comment on lines +142 to +144
instances.forEach((instance) => {
instance.$destroy();
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important to check as well

@@ -0,0 +1,18 @@
<template>
Copy link
Contributor

@camc314 camc314 Feb 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point in this file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although in single-file templates mutating the props of the children components work and it's reactive, in this DOM recyling approach we're working with real instances. Thus, we can't mutate props programatically, we can only mutate data (console errors appears and nothing happens).

This component serves as a wrapper of the card one (which takes item as a prop), but it has no props and takes all BaseItemDto using it's data. That way, we keep backwards compatibility for places where we don't need virtual loaders, like item pages, home page, etc...

@github-actions github-actions bot added plugins Pull requests that edit or add Nuxt plugins vue Pull requests that edit or add Vue files labels Feb 27, 2021
@sonarcloud
Copy link

sonarcloud bot commented Feb 27, 2021

SonarCloud Quality Gate failed.

Bug E 1 Bug
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@github-actions github-actions bot added merge conflict Something has merge conflicts and removed merge conflict Something has merge conflicts labels Feb 27, 2021
@stale
Copy link

stale bot commented Jun 2, 2021

Issues go stale after 60 days of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 14 days of inactivity. If this issue is safe to close now please do so. If you have any questions you can reach us on Matrix or Social Media.

@stale stale bot added the stale This issue or PR hasn't been updated in at least 60 days label Jun 2, 2021
@stale stale bot closed this Jun 16, 2021
@ferferga ferferga deleted the virtual-scroll branch January 13, 2023 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge conflict Something has merge conflicts plugins Pull requests that edit or add Nuxt plugins stale This issue or PR hasn't been updated in at least 60 days vue Pull requests that edit or add Vue files
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants