wearebraid / vue-formulate

⚡️ The easiest way to build forms with Vue.
https://vueformulate.com
MIT License
2.25k stars 245 forks source link

Repeatable group performance for lots of fields #171

Open engram-design opened 4 years ago

engram-design commented 4 years ago

Not really a new feature, but also not a bug! I'm having some performance issues with a repeatable field, and I've put together an example Codepen to illustrate it:

https://codepen.io/crawf/pen/qBbKEBJ

I've put a tiny delay on a loaded property so that you can see the form load and render, then see a visible delay of a few seconds until the list renders 50 x 4 input components.

Note that its intentionally large to illustrate my point. I'm using a schema, but just to test that's not a factor, here's a pen with normal components: https://codepen.io/crawf/pen/BajVNJx with much the same result.

In contrast to this same example with plain inputs - obviously a whole lot less overhead, so it might not be a fair comparison - https://codepen.io/crawf/pen/VwedLbm

Wondering if there's anything that can be done, or if it's just the reality of the situation?

justin-schroeder commented 4 years ago

Thanks for the good report. This is something I was aware of, but haven't spent the time to take a crack at the problem. The crux of the issue is reference types in Vue (of which arrays are, and groups are modeled to an array) generally cause Vue to re-evaluate all of it's computed props and watchers, there are some clever ways around this though like property comparison, which we use in a few places in Vue Formulate for performance reasons, but this logic has not yet been applied to groups (partly because it seemed a bit like an edge case to have this many items) — but its 100% something we need to try and fix.

The TLDR on that is that editing one field in a group causes all inputs in the group (group_inputs*n) to re-evaluate. If you’re interested in tackling the problem feel free to take a crack at it 👍

engram-design commented 4 years ago

@justin-schroeder I figured it was low on the list, its hardly an issue for 90% of people. We just have a rather large questionnaire type form, which is more or less the same as the codepen, with 4-5 fields per row.

I'll do my best to dive in and see what I can figure out! I know items get a __id private property set, is is a good idea to rely on that?

justin-schroeder commented 4 years ago

Yeah, since array values don't have their own keys, we assign a Symbol since it's a reference type to each array item in a group. It shouldn't ever change so it's the best way to reference individual ones.

However, I think the issue is in the way the group handles mutations. Each group "row" is basically it's own mini form with a registry and that logic needs to be fine-tuned to not cause the parent model to have a new reference when it's updated (unless we're adding/removing a row).

engram-design commented 4 years ago

@justin-schroeder I've spent some time trying to get my head about the internals of the registry, and its certainly something going on there with the computed properties. Removing them seems to make it significantly quicker, but obviously there's the behaviour lost.

It's probably a bit too much for me to handle at the moment, and my lack of knowledge/skill to be of any useful help. For the moment, I'm using regular inputs in the inner fields, which probably isn't a bad idea anyway. It's just a pain to deal with validation, as it has to be done at the group level (which I have a custom input for).

I'll see how things progress!

justin-schroeder commented 3 years ago

There are some significant architecture changes that would be required to significantly improve the performance of large groups that would require breaking changes to the API contracts already in place. For now I'd consider groups a great tool for small/medium size lists and not appropriate for large lists. In version 3, since we have the opportunity to implement breaking changes, we'll tackle those underlying changes to try and make groups high performance even at large scale. For now i'm making this a 3.x task.

JackEdwardLyons commented 3 years ago

Hi @justin-schroeder I really love using Vue Formulate and don't want to switch. I too have this problem with repeatable fields and have tried adding debounce but there are issues when pre-populating the form where the values inside repeaters sometimes don't populate.

Before getting to the Vue 3 release, is there a workaround for Vue 2 that I can implement to just get by? @engram-design have you found a solution?

Thanks again

engram-design commented 3 years ago

@JackEdwardLyons I actually can't quite recall how I solved this! I think I ended up using plain input elements in the rows, as Formulate couldn't handle things.

The code in question was for a table field. You can have a look at the code here - https://github.com/verbb/formie/tree/craft-3/src/web/assets/forms/src/js/components/formulate/table. It was for a Craft CMS plugin, so it wouldn't be the easiest to spin up again on your end to see how it all works, but there might be some useful bits in there.

The end result was something like this: image