DerYeger / yeger

Monorepo for @yeger/ NPM packages
MIT License
315 stars 24 forks source link

Optimize vue-masonry-wall #239

Closed Mivik closed 10 months ago

Mivik commented 10 months ago

Description

Recently I've tried several masonry plugins for Vue (vue-masonry, vue-next-masonry, vue-grid-layout) until I came across your work. It's amazing and perfectly meets my needs. Thanks a lot!

However, while building an infinite scrolling page using the masonry wall, I noticed a performance degradation, especially when the number of items exceeds ~100. Each load (which fetches around 20 items from the server) takes around 5 seconds to render and freezes the webpage.

Upon reviewing the logic, I found that the current implementation rearranges all items whenever there is a change. Also the current implementation does things like: measure all columns' heights, find the smallest one, insert item to it, await nextTick and measure all columns' heights again… This is really inefficient as the number of items goes up.

In this PR, incremental option (defaulting to false to avoid breaking changes) is introduced to render items incrementally. Specifically, if the keys of items given by keyMapper(item, 0, 0, index) remain unchanged, redraw will then arrange only updated parts.

Redraw strategy is updated as well. When redrawing, all columns' heights are fixed first (set explicit height in style and overflow-y: hidden), and all items are inserted into the first column. After awaiting for a tick, all items' heights can be retrieved and then the algorithm is performed without interacting with DOM.

In my scenario this significantly improves the performance.

Type of change

Might be a breaking change but I haven't tested thoroughly.

Checklist:

DerYeger commented 10 months ago

Thank you very much for opening this interesting PR! I'll have a look once I find the time in the next few days.

On main, there was an issue with Vue 3.3.10 breaking the props of this component. I downgraded Vue to 3.3.9 to "fix" this. Would it be possible to rebase your branch?

Mivik commented 10 months ago

Rebased

DerYeger commented 10 months ago

Sorry, I merged the PR by accident. I found some issues with the demo.

The Vue 3 demo seems to have unbalanced columns and the Vue 2 demo seems to be broken in general. I pushed a commit (which accidentally merged the PR, which I reverted) to apply formatting and use the incremental option for the demos, but that didn't help.

My added commit is on https://github.com/DerYeger/yeger/tree/feature/incremental.

Mivik commented 10 months ago

I'll try to fix it. BTW, I kept getting this when using pnpm dev in this repo. Any idea?

../../node_modules/.pnpm/esbuild@0.19.5/node_modules/esbuild/lib/main.js:1374:27: ERROR: [plugin: externalize-deps] Failed to resolve entry for package "vite-plugin-lib". The package may have incorrect main/module/exports specified in its package.json.
DerYeger commented 10 months ago

Can you try running pnpm build once before that? That should build vite-plugin-lib.

Mivik commented 10 months ago

Can you try running pnpm build once before that? That should build vite-plugin-lib.

Thanks! That helped.

The unbalanced columns result from a design bug. Within the redraw function, we employ await nextTick to obtain the heights of elements. However, this could lead to the re-entrancy of redraw, causing two redraws running simultaneously.

A straight-forward solution would be setting a re-entrancy flag, but the redraw request issued during the redrawing process would be ignored. It might take some time for me to find a better solution. Do you have any ideas on how to address this?