One entry in this space that doesn't get a lot of attention is ASP.NET Blazor [1]. Blazor gives you the option of writing views in C# that will actually compile to WebAssembly and run in the browser, or run on the server and send DOM updates over a SignalR connection, a lot like LiveView. [1] https://docs.microsoft.com/en-us/aspnet/core/blazor/
If Not SPAs, What?
81–90 of 456 posts
Re: If Not SPAs, What?
#82One entry in this space that doesn't get a lot of attention is ASP.NET Blazor [1]. Blazor gives you the option of writing views in C# that will actually compile to WebAssembly and run in the browser, or run on the server and send DOM updates over a SignalR connection, a lot like LiveView. [1] https://docs.microsoft.com/en-us/aspnet/core/blazor/
Last time I looked it couldn't run in the browser, and there was no ETA, has that changed?
Re: If Not SPAs, What?
#83Earlier quoted context omitted.
Agree completely. My only concern is that so many imitations are attempting to pop up in other languages and you just can’t do it as effectively. There are so many tradeoffs present that happen to result in the necessary set of functionality to do this efficiently that aren’t easily present outside of the BEAM.
What about this can't be done with async/await?
With BEAM, robustness is a special feature. In the BEAM you can kill, restart and replace processes all over the place and everything stays working pretty well, because its structure means everything written for it is designed with that in mind.
In a typical async/await server side application sharing state across clients, killing, restarting and replacing usually means the whole single process containing all the async/await coroutines, and the fancy per-client state you were maintaining is lost.
You can of course serialise state, as well as coordinating it among multiple processes, but that takes more effort than just using async/await in a web app, and often people don't bother. Doing that right can get tricky and it requires more testing for something that doesn't happen often compared with normal requests. So instead, they let the occasional client session see a glitch or need reloading, and deem that no big deal.
Re: If Not SPAs, What?
#84It surprises me that there are so few implementations using this pattern today.
The Meteor pattern is another good one for the user, if you like things better optimised for user-visible latency (as user interactions happen at the client first), but it hasn't ended up with much mindshare now. Perhaps because it's more complicated to use, and perhaps because JavaScript isn't ideal for this.
Svelte's precompiled diffs and minimal client-side code is another pattern I like.
For an application I worked on, I developed a combination of those three patterns. It had a nice and very efficient combination of server-side rendering and client-side immediate interaction, without needing custom JavaScript. This made it fast, user-friendly and pleasant to develop in.
Like LiveView, the client would send user events to the server, which would update affected component model state (which could be shared among different clients too). Server would re-render any visual components which used the model data (using automatic dependency tracking from previous renders), and then send an async event to each affected client containing an efficient diff of all rendered changes. If there was much network latency or too much queued, multiple updates would be batched together, merging their diffs efficiently. The result was the update could be worst-case the size of a page replacement for any number of updates, and at best the size of a few DOM edits in place.
But, a little bit like Meteor (but without custom JS), the server could also push "client event-handling hints" in the rendered model to the client. If an event pattern-matched one of these hints, the update could be applied immediately on the client as well as the event sent to the server. The server knew what update would have been applied, and could account for it in the diff. So if the server's new rendering matched the client's immediate rendering, the diff was empty and didn't need to be sent! But if the server got a different result, for example the event caused an operation which failed, the diff caused the client to receive an appropriate correction.
The diffs were calculated efficiently too, not by rendering components and comparing trees, but by precalculating diff trails from state change patterns where those could be recognised. For example when one model value changed, if the diff would always be just a string change in the DOM, it already knew and emitted that diff directly. This meant diff calculation time was as fast as possible in simple cases (event to output in O(1)), and reduced to a reasonable diff time in the most complex cases (such as merged diffs).
Keeping it always perfectly in sync and always efficient, throughout network outages, reordered requests, lost events etc was quite technical, but it wasn't application specific. Only the framework needed to deal with the technicality.
This was lovely, because it always stayed in sync, it was always efficient for any combination of initial loads, reloads, and sync events, both on slow and fast networks, it worked without JavaScript if needed, had client-side immediate updates for simple logic of a general-purpose nature specified by the server-side, could share model state among different clients and pages, and still didn't need any custom JavaScript for the application.
As the server side was written in async coroutines and Perl, it didn't seem worth the effort of packaging and publishing that implementation as Perl's popularity declined (and indeed coroutines seem disfavoured there as well, even though they work brilliantly).
I still think that's a lovely interactive webpage model, possibly the best. Nicer than client-side SPAs and nicer than server-side rendering. It's pleasant to develop for, pleasant as a user as well, very fast in many different scenarios including poor network connectivity, and great for sharing live state that appears in multiple pages or documents.
I'd like to implement that again in a modern language, but I haven't had any compelling reason to work on a webapp lately.
Re: If Not SPAs, What?
#85You can achieve near SPA performance with XSLT in the frontend, and stitching multiple XMLs using it. - No JS fallback by default - Naturally friendly to XML database backends - Huge cache hit improvements - Effectively, you get the functionality of the now abandoned "seamless" iframe attribute
Re: If Not SPAs, What?
#86Earlier quoted context omitted.
Agree completely. My only concern is that so many imitations are attempting to pop up in other languages and you just can’t do it as effectively. There are so many tradeoffs present that happen to result in the necessary set of functionality to do this efficiently that aren’t easily present outside of the BEAM.
What about this can't be done with async/await?
Re: If Not SPAs, What?
#87Well, we could go back to the original model for the web, using REST & HATEOAS without arguing or even really thinking about it, by building hypertext-based applications. Of course, the hypertext we have, HTML, wasn't completed, and it leaves a lot to be desired. I have tried to fix that with htmx: https://htmx.org
One quick question: How should I be handling server side errors with this? Like, normally I’d check the status code coming from the server and throw up an error modal if it’s an error - what’s the equivalent mechanism here?
Re: If Not SPAs, What?
#88IMO, TurboLinks + service workers are the way to go. Not many people know this, but a service worker (previously called "local server") allows you to run a little web server in the user's browser that intercepts requests to your own web site. (There's no open IP port.) The service-worker web server can proxy requests to the remote server, and even build/store entire pages on the client side, enabling offline support.…
Re: If Not SPAs, What?
#89I think Phoenix Live View is maybe the most compelling story around this ( https://github.com/phoenixframework/phoenix_live_view ). I'm moving a side-project from React/SPA to Phoenix live view and it's kind of amazing to get the dev ergonomics of a server-rendered page with the UX benefits of a SPA. This course is a pretty great intro: https://pragmaticstudio.com/phoenix-liveview
Re: If Not SPAs, What?
#90- Rails
- StimilusReflex
- view_component (https://github.com/github/view_component)
- Web components
With these 4, it divides responsibilities very cleanly/pragmatically: Rails is your app framework. view_component is how you divide your view into an organized/flexible structure. StimilusReflex is the "reflexive" bridge between the 2.
What happens when you need just a sprinkle of javascript to re-render a component when websockets/StimilusReflex are too slow (ie: user interacting with a color picker or something)? You could use Stimilus.js to sprinkle this interactivity... BUT you might end up with duplication between your view_components and your Stimilus.js controller. If you use webcomponents, then you can follow the open/closed rule: Your view_component can only interact with your custom webcomponent and then your webcomponent knows how to render/re-render those custom bits. So it doesn't matter if that webcomponent is rendered from: initial page load, StimilusReflex, or rapid js events (before they are throttled to StimilusReflex), it's all goes through the webcomponent "front door".