mononym
I’m noticing quite a bit of slowdown when appending messages and replacing elements on a LiveView webpage which has an ever-increasing number of chat messages being displayed.
Each incoming message is wrapped in a p tag, and there are multiple spans within the text inside that p tag. As I start getting up to several hundred messages, replacing the input/form with a new one by using a new changeset starts to take some significant time, and the insertion of messages into the window starts creeping up to ~50ms just because of a reflow being triggered. The process of fully handling the form/input replacement can work its way up to 500ms or more.
Using some manual editing, I took those hundreds of messages and modified them so they were all part of a single huge p element on the page. Same number of text characters and ‘lines’, but just in one p tag instead of hundreds. This dropped the time to swap out the input field and insert new messages considerably, to a level which (while noticed) is at least acceptable. If I remove all the messages and have a blank slate, the insertion time is sub 15ms.
Does anyone have any tips/tricks they use with having a large amount of text and doing a lot of appending? Do I need to create some javascript hooks that go in and shuffle around the text LiveView pushes to the page so that each incoming message ends up in a single p element with all the other messages? Should I just accept having even less text available and only allow ~100 messages (this really isn’t workable for me honestly) to live at a time? Should I switch to channels and cut out LiveView dealing with sending messages to and from the client, leaving LiveView to handle other parts of the UI that aren’t chat related?
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ai
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #metaprogramming
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
Exadra37
You may want to take a look to the video from Chris McCord where he builds a Twitter clone and see how he is appending tweets, instead of triggering a full render of all tweets in the page:
Unfortunately he doesn’t provide the link for a Github repo, but if you take a look to the topic Where is the chirp repo for the real-time Twitter clone in 15 minutes by Chris McCord? you will find several links to different repos and discussion about it.
mononym
I am already using temporary assigns and appending to the DOM. The update happens exactly as in that video with small chunks of html being sent over and the update happening very quick…when there are a few items on the screen like in that demo.
My issue isn’t triggering a rerender of all messages from the perspective of the server, but that slipping these tiny chunks of HTML into the DOM is triggering a browser redraw in some cases and it’s causing huge spikes in apparent lag due to the browser freezing up while it scans the DOM to recalculate stuff.
Now what’s a bit odd is that while replacing the form in place takes a huge amount of time, I can perform a click interaction elsewhere on the same page that inserts and removes elements and it takes almost no time at all. So the redraw being triggered and it taking a while is happening only in a couple cases, apparently based on the relative position of those elements to the huge list of messages I am inserting as that last quick case happens “to the side” of where the messages are displayed.
This may very well be a browser limit thing I can’t get around and I have to get fancy with some tricks like I mentioned in the original post (using JS to shift incoming messages into the existing single “mega message”) but I lose some flexibility that way.
Part of me is almost wondering if I’m going to need to keep the messages server side and allow the user to “scan” through their messages up and down using hotkeys and swapping out what messages I show based on state. Much how many client side JS libraries handle large collections to keep performance good.
Exadra37
So I am new to Live View and still in the learning process, but I was wondering if you are rendering each message as a
live_component, using socketupdateinstead ofassign, and at same time addingphx-update="prepend"to the for loop rendering the messages?Maybe you could share your code or repo so that the experts here can help you better then me.
mononym
I’m not rendering a series of live components as I don’t need that complexity for the messages. However, other than that I’m doing almost what I’ve seen done elsewhere. However, rather than injecting values into an html fragment, I instead build up the HTML elsewhere and send it over to LiveView to be injected raw, rather than having values interpolated in. So my message might actually be:
"<p>Goodbye <span class="emotion">cruel</span> <span class="place">world!</span><p>Skipping the rest of the layout because it’s not important, but this is in my client.leex file:
The return for my mount:
i-n-g-m-a-r
Are you sure this is the culprit?
Are you also doing other LiveView stuff?
For example, once I had some component rendering an inspect of some state and it was killing performance.
In the mean time I was investigating the wrong LiveView for problems.
Exadra37
It looks like you are defeating the programming model of Live View here that is built to avoid precisely this “complexity”.
From my understanding in a for-loop you will want to use a
live_componentso that live view can properly track the changes and be able to do its amazingdiffto only send the values that have changed to the client, not the html. Live view engine separates static from dynamic parts, but you are defeating that in your code, or if you prefer you are working against the program model of live view.mononym
The culprit is a large number of individual DOM nodes on the webpage causing a very long redraw operation by the browser. ~50ms just to insert a single message into the
divcontaining all of thepelements containing the individual messages.Appending a message is pretty quick, relatively speaking, but swapping out the form/input by creating a new changeset (which triggers LiveView to send over a new form HTML fragment) takes over 500ms when we’re talking hundreds of messages. This happens because on my webpage the design is that the window containing messages sits directly above the text input field, so replacing the form means the browser takes a look at everything above it and it forces a redraw calculation of the form, the window containing the messages, and the hundreds of messages within the window.
I have a completely different LiveView interaction on the same webpage that happens in another ‘column’, and it’s still quick even with hundreds of messages. So the culprit is absolutely browser redraws/calculations when elements are replaced/added to the page in such a way that things have to be recalculated.
This is why I asked how people are handling having large numbers of messages when using LiveView and appending/updating the page, because that’s where the issue comes from.
LostKobrakai
To be fair this is also a problem with pure client side libraries and the reason why e.g. many client side frameworks have components, which render just the visible rows of lists instead of every available item.
mononym
I think you are misunderstanding LiveView, what it is, and what it is built on. The templating system EEX which LiveView is built on has been around since much earlier Erlang. All of the templates you see, whether the normal EEX templates or the LEEX templates, end up being processed and turned into strings after which is where LiveView comes in.
Using EEX absolutely does make it easier to write things like loops and create complex interfaces, but that has nothing to do with LiveView and is not a functional feature of LiveView at all. LiveView works just fine if you take EEX out of the equation entirely and write everything by hand.
For loops have nothing to do with Live Components. A Live Component is simply a way to encapsulate state (in some cases) and display logic in an easily reusable way, much like templates. That demo only used LiveComponents because it was effectively writing a bunch of cards to the webpage, each with like buttons and other interactivity. If you don’t need to encapsulate state, because you’re just writing out some text, you don’t need to use Live Components. That’s a red herring.
LiveView is about replacing JS as much as possible to provide richer UI experiences and to provide for a whole new set of interoperability between client and server out of the box while keeping as much server side as possible to reduce context change, development context switching, and improve the security and integrity of data. All of the templates, for loops, and everything else that makes building the actual HTML easy is outside LiveView’s bailiwick.
mononym
Which I called out explicitly in the initial post. And hence my asking, in the first post, how people are approaching it and even gave the idea of simulating that using hotkeys and “scrolling” through the messages that way.