I have a kind of unusual concern about LiveView: Bandwidth. In the demo they were using 60fps or html every second something of that kind would put significant load on network interface. Consider pushing new html code on every single change and doing diff on client side will amount to significant network usage compared to JSON or XML. This might not be a client side problem but might become issue for load balancers and also specially when bandwidth is one of the most expansive part of cloud.
It’s one of the problems I’ve thought about. You might be interested in this post: https://elixirforum.com/t/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/
Regarding performance, I think it might be nice to do some benchmarks to see where the limit actually is. In the JS community, there have been countless attempts to compare React vs Vue vs Angular in every which way. One that I remember somewhat vividly was React Fiber rendering a Sierpinski triangle: https://github.com/claudiopro/react-fiber-vs-stack-demo
On my machine, React Fiber hovers around 60fps (Chrome performance tab, record); where Stack hovers around 2fps. Any chance we could create this in LiveView and see how it compares?
LiveView is vaporware as of now… My musings about minimizing data transfer are merely theoretical and should not be taken as advice on how to efficiently diff HTML on the client
The 60fps was to showoff the performance of the approach, not to promote using LiveView for sending an html page every 16ms . Performance wise, we’re planning to perform diffing on the server to only send the bits that have changed.
Some things that pre-date this in one form of another, Nitrogen on erlang and Wt framework on C++. nitrogen let you define a page and then react to it similar to Drab’s low level interfaces, not as ‘live’. Wt for C++ on the other hand has been around for a LONG time, it basically gives you a kind of normal desktop GUI API (QT’ish) for working with the web, been around since 2005, and it is very much like liveview in functionailty (it was SO nice to work in!).
Nitrogen, due to it’s more low-level style, only did explicit updates. Wt was full observer-oriented, super-efficient. Drab.Live is mostly observer style though with some slightly heavy pre-generation (still less than a VDom).
It is in my stuff! ^.^
Unpoly and Drab both make it pretty easy to do though.
I’m curious, how is that new state transformed to the client? Is it a template replacement on the client, a VDOM merge, direct observer interactions (most efficient of these listed but requires compiled-in metadata about the template on the server, which is entirely doable in Elixir), or something else?
What does it gain over Drab.Live as well?
Do note, I’ve not seen the video, I often can’t watch videos. ^.^;
Isn’t the entirety of Drab already?
Eh, I actually see it as the very old way of doing this. Going back 20 years all web pages were server driven, even with sessions and live polling and COMET and all. It’s just coming back around at this point. Even Wt was birthed as the epitome of that whole server-owned idea (it’s built and managed by a business, it’s only incidentally open-source, and it didn’t even used to be FOSS back when I used it).
It would really help if you watched the video as I believe most of your questions will be covered. We use morphdom to apply the DOM patch, without the need for vdom on the client. wrt to drab, there’s some really great work there, and I think we have similar visions, but with different approaches. Note: I’m only familiar with Drab from tangentially following the forum posts, but from what I’ve seen Drab focuses on “remote controlling” the page from the server, where its primitives revolve around poking parts of the page for update, peeking parts of the page for values, and using client-like primitives for setting DOM node props/attrs. With LiveView, we take the react model and place it on the server, where we focus on stateful processes that upon updating their state, transform that state into HTML, which then is used to update the UI. So our approach is more about focusing the UI and stateful views, where the update step pushes the diff down, versus a more direct coupling to server controlling the client DOM/page.
I see this point, but I also find it funny we have folks who both see this as yet another new way to do server apps, and also the way we always did them from the start. Both have valid points, but it’s interesting watching the different hot-takes
Well, I particularly disagree with this point for some reasons:
- I would not call opposite direction, @chrismccord explained very well that his attempt was to solve the same problem, which is the poor UX when you have too many page refreshes. So the direction is the same, it’s just a different path, and it’s totally valid to try out different solutions for the same problem.
- Just because big companies are pushing it to a certain way does not mean that’s the best solution.
- Just because it’s not “disruptive”, doesn’t mean it’s not good. I see what you mean: the concept is really close to turbolinks, but over websockets. Well, turbolinks was good enough for a long time, but then, some people started to complain about UX with that and try out new solutions, which is good! Very good! But if you try out a new solution, and it’s not better in some senses, the wisest thing to do is going back and maybe try something else closer to how it was.
I see it as the way we always did them from the start, but a little bit simpler, faster and reactive. I already love the way we were doing it from the start, and if you really made it better, I’m really excited to try it out! Thank you!!!
Are browser-based client applications important?
I envision a future where SPA stands for “Server Powered Apps”
The funny thing is some industries are actually exploring or heading that way - with dumb(er) clients and intelligent backends. Where? Perhaps where you’d least expect it… gaming
I think it’s a really big mistake to base your technology choices on what big tech companies are doing, because you’re not Google (unless you’re Google). It’s a mistake because it’ll lead you down a path of maximum resistance and guess work before you even know what you’re going to build.
Those big tech companies love talking about microservices and other technology choices because they are 10+ year old companies with thousands of developers, expert knowledge in their app’s domain, lots of sub-products and likely hundreds of isolated teams. It doesn’t mean what they are doing is the best overall choice. It means what they are doing works for the type of scale they are at.
When starting a new application, most of the time it’s just you as a single developer trying to get an MVP out. It may take years before you’re in a position to on-board 1 other developer and even then, splitting your back-end and front-end is not close to necessary at that scale.
I wrote about this topic a bit in the context of microservices but a similar philosophy applies to splitting out your front end and back end https://nickjanetakis.com/blog/microservices-are-something-you-grow-into-not-begin-with.
Are browser-based client applications important?
I can’t imagine how this approach would be any more fragmenting than the existing standard for rich clients – where you already have the initial server render, the client side code, and some API. LiveView just looks like a simple embedding a view within a view (which we already do in Phoenix), except the embedded view can by dynamically updated.
Clearly it doesn’t do anything technically that couldn’t be done in 2009, but to have all the tedious and error-prone plumbing taken away seems like a huge win to me. Big props to Chris and the Phoenix team for making this happen.
One big big distinction is we have stateful components on the backend, where turbolinks requests a stateless page/fragment. So for the simplest
CounterView example, turbolinks would need to have the client send an Ajax request every second to render the stateless view. For us, the view is up and running and can send updates to the client at any time, on its own accord, such as subscribing to platform events and updating the UI, phoenix presence, etc.
*.html.drab templates, which is a special eex template processor that builds up a call tree that will return the output like eex but also it compiles the information that Drab needs to know to perform real updates. From that point on when you update, say, an ‘assigns’ that was passed to the template, then it figures out what was chanes of it and then only updates those parts of the page (observer model), without needing to build up a VDom or anything of the sort, it ‘tags’ elements on the page via unique drab-id style id’s so it can directly reference them. In addition you can even do things like do sub-templates that you can put anywhere, like having a Notifications button at the top that renders with the current notifications (progressive enhancement) but will also receive updates to itself, only updating the part of the DOM that actually changed without iterating or recursing or re-rendering anything else.
This still sounds very much like Drab.Live based on your post (except Drab’s builds it’s template information for fast updating at compile time without needing to take html parts again), so I’m curious what the differences are.
Drab is a multitude of parts, at it’s most basic is
Drab.Live and everything in between.
You can see the demo page https://tg.pl/drab running a Drab server remotely (to me anyway, off in Europe or something somewhere), so it has a good half second latency to me and yet everything on it works properly. You can give it a try and look it over. It demonstrates many of its API’s, Live and others, but even the top https://tg.pl/drab#simple example is a simple
Drab.Live demonstration, first you make the template:
<form> <input name="text_to_uppercase" value="<%= @text %>"> <button drab="click:uppercase">Upcase</button> <button drab="click:downcase">Downcase</button> </form>
If you set a callback url it could even be progressively enhanced as well, but then you make the commander:
defmodule DrabPoc.LiveCommander do use Drab.Commander defhandler uppercase(socket, sender) do text = sender.params["text_to_uppercase"] poke socket, text: String.upcase(text) end defhandler downcasecase(socket, sender) do text = sender.params["text_to_uppercase"] poke socket, text: String.downcase(text) end end
sender contains the data related to the action, in a form’s case it sends the form, you can also specify other data to send with it or whatever as well. Then all it’s doing is updating the
assign in the template of the key
text with the mutated string. The Phoenix Controller for the page is no different than what you’d expect except for a use Drab thing at the top. It will then send the string to the template drab handler that will see where it is used and setup a minimal command to send to the client to update it, which for
<input name="text_to_uppercase" value="<%= @text %>"> will be something like
The ‘*.html.drab’ templates are parsed using Floki to get all information about the html, passing it straight through as a string otherwise, so it can build that information so it knows how to query the DOM. The
drab-ampere attribute is the unique ID of how to access that element that has an assign somewhere in it’s executed code (or higher up references, in the worst case it will re-render large chunks of HTML, but if you don’t do anything crazy in your view it is nicely efficient, but it has had a lot of work done to be able to figure out where and how things are used or to fall back).
But yeah, that above demo page shows off a lot of Drab, not just Drab.Live stuff but a little of all of it, but even just looking at the Drab.Live you can see how similar it is to every description I’ve seen of LiveView here so far? I might be able to watch the video this weekend, but at least based on the descriptions thus far it seems like LiveView is just rewriting Drab.Live (although perhaps more cleanly as rewrites often do, but PR’s can always fix up Drab and Drab has a lot of functionality as it stands built up over a long time).
Thus why I am exceptionally curious what makes LiveView so different from Drab.Live that makes usurping another projects major feature with near identical functionality (I havn’t seen anyone mention how LiveView would determine how to set an attribute or a property for example, among other things, on an element, which Drab can do among other things) in such an announced way.
Lol, I think it depends on how long people have been doing this. I started back writing perl CGI scripts way back in the mid-90’s, and I wrote my own BBS server with 3 dedicated connections before that! It was so fun! ^.^
The Shared Commanders in Drab let you create reuseable components (even many times on a single page if you want), which are especially cool (I use these excessively in some areas).
Which was your idea (AFAIR), which only shows how Drab evolved in with the help of community!
Honestly I ripped it from both Wt (Web Toolkit in C++ that is very much like Drab.Live/LiveView but even more… old and refined and C++) and Polymer. ^.^;
I used that feature heavily in both of those too.
I think I lied about drab in my talk at elixirconf - if there’s any way I can rectify my accidental lie let me know - I think I said something to the effect of drab only being targeted updates that didn’t behave like my library texas. I don’t have much of an audience or I’d just tweet a correction or something
this_commander looks very jQuery. When I want to update the first span from within the second commander, is it possible to get first commander reference? If so, it’s going back to jQuery problems.
I may not understand Drab much ^_^!
this_commander is just a drab function that returns a string that is the commander’s unique topic, it’s nothing but a helper (though a very nice helper). Not similar to jQuery even remotely.
What do you mean by first commander? You can get any reference on a page by building a name for it if you need, though cross-commander talk should really always be done via broadcast messages and assigns.
Drab is actually really easy to use, especially if you keep to just Drab.Live (which is what I do 99% of the time until I need to interact with the unpoly.js library).