With this framework's model, the process would be change -> paint -> change -> paint etc. etc. Sure, you get nice FPS (because it's painting a LOT), but that will destroy battery performance on mobile phones and I don't need 60 FPS in my shopping web app.
How I built a fast JavaScript framework
41–50 of 68 posts
Re: How I built a fast JavaScript framework
#42This looks similar in many ways to mithril[1] -- somewhat similar api - mount, view, r("input") vs m("input"). The counter example[2] looks similar[3] I'd be interested in seeing comparisons between the two. [1] https://mithril.js.org/ [2] https://github.com/MithrilJS/mithril.js/blob/5956314e3655a3c... [3] https://github.com/radi-js/radi/blob/master/examples/counter...
In terms of API, frankly all the frameworks are more or less similar. Mithril has been around for a while, so obviously all the common observations apply (i.e. it's been more battle tested, it has a more mature community, more libraries, etc)
Taking a quick glance at the Radi.js code, I noticed a few things that could still use some improvements (e.g. the comment regexp someone else had pointed out, the handling of attributes not accounting for things like SVG, etc)
I didn't see support for lifecycle methods in the Radi.js code, and it's not clear to me from the docs whether keyed lists and fragments are supported. Keyed lists are extremely important in cases like `someArray.unshift()` and any list containing stateful dom (e.g. inputs or link tabindex). Lack of fragments would not be a deal breaker, but IME they're super nice for building lightweight abstractions, especially in Mithril's case, where they can have lifecycle hooks.
Re: How I built a fast JavaScript framework
#43Earlier quoted context omitted.
This isn't quite correct. Modern rendering libraries aren't going to recreate an input from scratch unless they absolutely have to, vdom or not. What's tricky about reconciling app state changes and dom state is dealing with things like cursor position if e.g. the input value change comes from a socket and the user had the input focused and the cursor in the middle of the text. But this has nothing to do with vdom
To prove you wrong, consider how DOM elements are created (remember in Surplus there are no virtual elements). Ignoring all dependency tracking, the code to show some text has to be (eventually) of the form x = document.createTextNode(v); Now, consider the dependency v changes. The only option is to run the code above again, in its entirety. And thus, the DOM node corresponding to the text is new . Of course, a smart…
Re: How I built a fast JavaScript framework
#44Earlier quoted context omitted.
This isn't quite correct. Modern rendering libraries aren't going to recreate an input from scratch unless they absolutely have to, vdom or not. What's tricky about reconciling app state changes and dom state is dealing with things like cursor position if e.g. the input value change comes from a socket and the user had the input focused and the cursor in the middle of the text. But this has nothing to do with vdom
To prove you wrong, consider how DOM elements are created (remember in Surplus there are no virtual elements). Ignoring all dependency tracking, the code to show some text has to be (eventually) of the form x = document.createTextNode(v); Now, consider the dependency v changes. The only option is to run the code above again, in its entirety. And thus, the DOM node corresponding to the text is new . Of course, a smart…
Not at all. The reactive pattern typically boils down to something like this:
el = document.createTextNode(initialValue);
parent.appendChild(el);
reactiveValue.onchange(v => {
el.nodeValue = v;
})
Ironically, in this specific example, vdom libs typically do this when possible: parent.textContent = v
which does in fact replace the underlying text node, but they do so for the sake of performanceRe: How I built a fast JavaScript framework
#45I'm not a frontend developer and don't know really know JavaScript frameworks. That said, it seems like something very well established like React would choose to have a virtual DOM for a good reason. Surely there must be some tradeoffs that the author makes for his design to get better performance. Could somebody explain the cost/benefit of doing things one way versus the other?
Virtual DOM was invented not to make things faster than with jQuery, etc., but to make a better and less error-prone style of web app development not be intolerably unperformant. I think if we could see a larger Radi example, say, the standard TODO app, the costs of its approach would become obvious.
Re: How I built a fast JavaScript framework
#46Earlier quoted context omitted.
Basically a table but instead of HTML elements, using CSS properties? I loathe how "let's move the concept from one separation layer into another one, or better yet - let's mix the concepts together in the same separation layer" is becoming the next big thing recently.
CSS Grid does the opposite of what you claim – it fixes the mixing of concepts that previously tied HTML elements to their layout, not make it worse.
Re: How I built a fast JavaScript framework
#47Earlier quoted context omitted.
Virtual DOM was invented not to make things faster than with jQuery, etc., but to make a better and less error-prone style of web app development not be intolerably unperformant. I think if we could see a larger Radi example, say, the standard TODO app, the costs of its approach would become obvious.
That's not true. Virtual DOM was created to avoid recreating huge DOM elements each time a re-render is needed, which was the way apps used to be done in the days of jQuery.
Templating engines such as handlebars provide a declarative abstraction that represents how the DOM is supposed to look like given some rules, rather than procedurally specifying how to get from A to B. This is similar to how you write HTML declarative as opposed to how you have to write canvas code procedurally.
Virtual DOM is just an implementation detail of some templating libraries that have the same goal of providing that declarative abstraction. The problem was that popular KVO systems (e.g. Knockout and Ember) were notoriously slow and people were looking for alternative ways to speed up declarative rendering libraries. The community has since learned a lot about performance and is realizing that KVO systems _can_ be performant after all.
The main drawback of KVO systems is that their API bleeds into the data layer because they require you as a developer to manually register data as observable entities. In contrast, virtual DOM works with plain Javascript objects which are much more familiar and easier to manipulate and interop with. A third approach, called dirty checking and popularized by Angular also had the desirable property of working w/ plain js objects, but it was also very slow. Some systems (most notoriously Vue 1) tried to bridge the gap between KVO and POJOs by providing a POJO-looking data layer that has monkeypatched array methods etc that provide observability/reactivity under the hood. Without JS proxies, though, this still left some weird edge such as the ones handled by `Vue.set`/`Vue.delete`. Vue eventually moved to vdom because the way it mounted subcomponents didn't scale for recursive mounts (think comment trees such as the ones in HN or reddit). This is because it had to mount each level to find out what subcomponents needed to be mounted, and as well know, multiple repaints is a big no-no for perf.
With that said, virtual DOM is not perfect either. It's inherently memory intensive and can block the UI in cases with large DOMs and few changes. Various frameworks have various techniques to cope (such as shouldComponentUpdate and friends) but they typically require developer intervention, whereas a KVO system would not suffer from this class of problems to begin with. Another problematic aspect of virtual DOM systems is that they are "naked" js and thus, difficult to optimize as libraries from an algorithmic standpoint. This is why Svelte does better in benchmarks than vdom systems.
TL;DR: if you want to evaluate the performance characteristics of a KVO system such as Radi or Surplus, the main things you should ask about are API lock-in in the data layer and performance of recursive mounts. If you want to evaluate perf of virtual DOM systems, you should ask about diff latency for needle-in-a-haystack scenarios and ease of use for hooks to selectively diff a tree.
Re: How I built a fast JavaScript framework
#48Re: How I built a fast JavaScript framework
#49Earlier quoted context omitted.
To prove you wrong, consider how DOM elements are created (remember in Surplus there are no virtual elements). Ignoring all dependency tracking, the code to show some text has to be (eventually) of the form x = document.createTextNode(v); Now, consider the dependency v changes. The only option is to run the code above again, in its entirety. And thus, the DOM node corresponding to the text is new . Of course, a smart…
> The only option is to run the code above again, in its entirety Not at all. The reactive pattern typically boils down to something like this: el = document.createTextNode(initialValue); parent.appendChild(el); reactiveValue.onchange(v => { el.nodeValue = v; }) Ironically, in this specific example, vdom libs typically do this when possible: parent.textContent = v which does in fact replace the underlying text node,…
Re: How I built a fast JavaScript framework
#50Earlier quoted context omitted.
> The only option is to run the code above again, in its entirety Not at all. The reactive pattern typically boils down to something like this: el = document.createTextNode(initialValue); parent.appendChild(el); reactiveValue.onchange(v => { el.nodeValue = v; }) Ironically, in this specific example, vdom libs typically do this when possible: parent.textContent = v which does in fact replace the underlying text node,…
Yes, you can code like that, but ultimately that block will be contained inside a closure that will get triggered by some change. Of course, unless you are very careful.
In the following example, I change the button id on click. It's still the same button element after the change: