LiveView patching huge DOM/embeds_many speedup?

Hello everyone!

We decided to move our CMS admin/backend from a Phoenix/Absinthe/Vue setup to a LiveView setup last year and the change has been really great. Much easier to reason about client errors and a lot leaner overall.

Our only issue is this:

We have a “block builder” for building up page content. This is a data JSON field in the db and is a list of different block modules. Each of these block modules is a Live Component. The user can pick and choose modules, drag and reorder, disable and edit. When we tested it out before making the leap to LiveView we saw that it could introduce a liiiitle latency when adding new blocks to a fairly large list of blocks, but it was not a problem at all.

However, some of our clients really like building HUGE pages. This in turn results in a HUGE DOM, and patching it on phx-update gets pretty slow. Also since I believe embeds_many fields in forms aren’t really change tracked (at least it sends very large payloads on every update), every time the user stops typing in a field in this huge form, there is a noticable delay after the debounce when the browser is patching the DOM.

If I enable profiling for the live socket and type “test” in an input:

premorph container prep: 0.0078125 ms
live_socket.js:227 morphdom: 96.622802734375 ms
live_socket.js:227 component patch complete: 745.5849609375 ms

Those 745ms are blocking the UI so it feels like it freezes up.

If I profile it with devtools:

Screenshot 2022-03-24 at 10.13.57

Are there any alternate solutions for this for us? I don’t think we can phx-update="ignore" the block builder and keep our own state/updates since every block is a Live Component.

Here is a little video if that maybe illustrates the setup better:

2 Likes

You may need to make a custom latency compensation like mechanism through hooks.

I’m still learning LiveView, but this symptom reminds me of the discussion under “Pitfalls” in the LiveView assigns guide.

2 Likes

I’d suggest to break up the big form into many smaller forms.

2 Likes

I don’t think there is a way around the huge payloads when it comes to forms yet — at least that’s what we’ve seen while testing. We have followed the pitfalls guide and made sure we don’t do any of that stuff :slight_smile:

Interesting. We could perhaps at least split out the block builder to its own form and stitch them together on save maybe?

1 Like

This is semi-inline with that idea:

Many forms in the page communicate payloads upward to a “form controller coordinator” that manages the actual “yes you can save it” bit. Each form is a component so they are in the same process, so there isn’t too much work to glue them all together (though gosh darn it, elixir is so good, I have used pubsub channels to communicate between forms in different views with basically zero effort). You may have a few coordinators?

2 Likes

Thanks for this!

I need the forms to coexist on the page, will LV still scan through everything looking for stuff to patch? My DOM will remain huge, but it will at least get faster when updating inputs in the “smaller” form, if I understand correctly?

I can split out the block editor, but I can’t really split the block editor itself into smaller bits I think. Or, at least, I’d really like to avoid that :thinking:

Huge DOM is not really a problem; huge DOM diff is. Another trick that could be useful is if there is a large piece of content that is being removed, but there is a decent chance that the user would like to see it again, don’t delete it from the DOM; just hide it, so you can effortlessly show it later with a flip of a CSS class.