Ooh, LiveView pattern implementors! I have a question -- I've been tinkering with a custom Phoenix LV client in my 'shop time', so I wanted to ask: are you doing your diffs on a string-based representation, like they do in the internal Phoenix LV protocol? Or are you sending the DOM patches?
In the phx protocol, their diffs depend on having split the render into a tree of 'statics', 'dynamics', 'components',
and on each change, that structure is patched, and needs to be recursively zipped to a string, parsed into a DOM, and patches computed against the previous DOM.
I've been playing with a new client as a way of understanding it, and it's definitely a certain amount of work! Fortunately computing 'what needs to change to reconcile 2 html-like DOMs' is a project that's been wrapped into libraries a few times in a few languages (phoenix relies on other people having done that too, via the 'morphdom' library in JS, and Dockyard recently open-sourced an ergonomic Rust library I'm using in my client).
So -- it's a lot of client work, but it optimizes for bandwidth. Cool.
But I've wondered if there could be any benefit to optimizing for granularity of patches, and simpler clients -- by sending 'just the patches', ie the list of DOM operations that cause the desired end state. My intuition is that the bandwidth 'cost' would be prohibitive, because the list of patches required to perform the morph could be quite large for a change that appears small in 'what string has changed' terms. (maybe harder to debug, too? lots of trade-offs)
I just don't have the energy to try it that way too, hehe, on the off chance that my intuition is off base and it'd actually help improve things (simpler clients, and maybe the bandwidth hit wouldn't be too bad?); it'd require deep server changes as well, when up until now I've only been writing a client. So I was curious what approach y'all are taking.
Prior/related art in the Rust space: I know there's a non-Phoenix liveview project, 'dioxus', with a generic DOM liveview implementation that several clients have been created for, by making a state machine that operates on that list of generated Patches; however I don't know what that one sends, whether DOM patches directly, or Phx-style diffs that need to go phxDiff -> zippedUpString -> dom -> morphPatches -> applyToDom.
We take the Phoenix approach of splitting the DOM into static and dynamic parts. In fact, we even re-use the pheonix.js library on the frontend, so we just implement the backend part that speaks the same protocol as Phoenix.
I don't think that sending DOM patches directly would have a significant higher bandwidth cost, but it would have higher "implementation" cost for us. That's why we choose to just use an existing "battle tested" library.
Oh, hey that's great! So in your case, you're re-using the Phoenix client, and thus the Phoenix protocol lives on so as not to break that. Makes sense.
Thanks for the answer -- the current SDUI renaissance is a beautiful thing. Cheers :)
Didn't mean to distract from the main discussion either -- pre-emption is something that more people should want, I think, so y'all making it the default for WASM stuff feels super valuable. And surely the sandboxing is really valuable to some people too. (But day-to-day I probably won't use Lunatic, whereas LiveView-based SDUI is my current shop-time project).
In the phx protocol, their diffs depend on having split the render into a tree of 'statics', 'dynamics', 'components', and on each change, that structure is patched, and needs to be recursively zipped to a string, parsed into a DOM, and patches computed against the previous DOM.
I've been playing with a new client as a way of understanding it, and it's definitely a certain amount of work! Fortunately computing 'what needs to change to reconcile 2 html-like DOMs' is a project that's been wrapped into libraries a few times in a few languages (phoenix relies on other people having done that too, via the 'morphdom' library in JS, and Dockyard recently open-sourced an ergonomic Rust library I'm using in my client).
So -- it's a lot of client work, but it optimizes for bandwidth. Cool.
But I've wondered if there could be any benefit to optimizing for granularity of patches, and simpler clients -- by sending 'just the patches', ie the list of DOM operations that cause the desired end state. My intuition is that the bandwidth 'cost' would be prohibitive, because the list of patches required to perform the morph could be quite large for a change that appears small in 'what string has changed' terms. (maybe harder to debug, too? lots of trade-offs)
I just don't have the energy to try it that way too, hehe, on the off chance that my intuition is off base and it'd actually help improve things (simpler clients, and maybe the bandwidth hit wouldn't be too bad?); it'd require deep server changes as well, when up until now I've only been writing a client. So I was curious what approach y'all are taking.
Prior/related art in the Rust space: I know there's a non-Phoenix liveview project, 'dioxus', with a generic DOM liveview implementation that several clients have been created for, by making a state machine that operates on that list of generated Patches; however I don't know what that one sends, whether DOM patches directly, or Phx-style diffs that need to go phxDiff -> zippedUpString -> dom -> morphPatches -> applyToDom.