How I built a fast JavaScript framework(medium.com) |
How I built a fast JavaScript framework(medium.com) |
Like for this one from a peek at the source code it appears to try to parse JavaScript at runtime using regexes (https://github.com/radi-js/radi/blob/master/src/index.js#L4) which is great for demoware and not at all something I'd want to use in production, but that is not mentioned anywhere in the site or blog post.
I don't mean to just dump on this project in particular, which could just be a fun toy side project for the author, because this problem is endemic. (But then why go to all the effort to make all the marketing materials for it without actually writing any docs?)
It parses the source code of the view function at run time, matching parentheses, but ignoring whether those are present in strings or regexes. Likewise, the comment stripper will turn
"/*whoops*/"
into the empty string...In other words, the parser is buggy :-/
If you run the React Fiber demo, you'll see it's just as fast: https://claudiopro.github.io/react-fiber-vs-stack-demo/fiber...
Exactly, I'm using react at work when working on a huge web app and jquery at home for my little side projects. It's so nice not having a build step for my side projects. While at the same time it's really nice not using jquery in a project with dozens of developers.
The only thing wrong with jQuery is that it's perfectly fine until it isn't. Conversely, if you'll never need React's power you don't need it's complexity either.
Another issue was the rapid deprecation of age old JQuery plugins and the lack of equivalent plugins in the React landscape causing confusion whether you should build it yourself or not etc. I had hoped WebComponents somehow got standardized as the underlying architecture for SPAs, just so that the plugin/widgets wont get fragmented further, but it looks like it wont be happening anytime soon.
I feel this quite often. It doesn't help that the tools move so quickly that once you develop a kind of gut feel for what frameworks work in what situations its already changed and you get a bunch of "lol [X] was 2 years ago we're all on [Y] now".
Cool, but things like this have a marginal positive effect for developers, at the cost of having to learn yet another thing, and making it harder and harder to make a reasonable webpage.
But the fact that we have become dependent on frameworks that solely exist in JavaScript and external tooling, created by mainly large organizations, using techniques that work around the basic browser behavior, had me worried for a long time. I say that as someone who has been building websites and web applications since around 1997.
The flip side of this is, without all that experimentation being done by people outside of the standard process, perhaps discoveries like this one (how to do something similar using essentially standard tech) wouldn't be found. And the progress we made before the latest round of shadow and/or virtual DOM and SPA applications is what enabled us to get to the point where we could fully conceptualize them.
So there you have the interplay between the community, industry, de facto and formal standards in a nutshell. There is good and bad, especially when you are in the middle of massive technology shifts.
I for one welcome rolling some of the advancements that we have discovered into formal standards that become built into and fully supported by browser vendors. I welcome a return to "how can we simplify" where we've gotten to and roll that into the standard realm more.
And personally, if we can simplify the use of external tooling as much as possible... I think that would be a huge boon. That tooling is, I believe, going to be extremely brittle over the long run and is a risk factor.
I wish the framework developer the best of luck and thank them for looking back at what we could already do in an attempt to simplify!
Btw, I think that what I am talking about is basically the process of "exnovation" [1].
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?
I think if we could see a larger Radi example, say, the standard TODO app, the costs of its approach would become obvious.
Of course, you can recreate DOM elements with their complete state, but if you'd try to do that with e.g. a VIDEO element, then you risk flicker and reloading of the actual video.
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
Surplus is fast because it doesn't have many features that available in many other libraries. For example, component model with lifecycles.
In this benchmark, "select row" should be the best case scenario for library like this, but even in such scenario it is slower than some vdom libraries.
[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.
Outside, with Mithril you don't need the l() function to bind attributes to vdom nodes. I also suspect there are things this framework can't do that vdom frameworks like Mithril can.
Aside, this framework's author seems better at marketing than the Mithril community. Notice the multiple comments on this article lamenting how React's size and complexity make it less suitable for small projects even though it has advantages for large ones. Mithril is suitable for both large and small projects, but you'd think it didn't exist.
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.
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.
Even if we pretend that other architectures doesn't have any memory overhead, vdom memory overhead in a large UI app will be less than a single small decoded image. But in reality, KVO can be way much more memory intensive, especially when we consider cases that involve filtering items in large collections.
I'd imagine closures also break.
I hadn't thought of minification, yes it pretty bad :-/...
It looks like the author is smart but has little JS experience (`var last = temp.splice(-1)[0]` where `temp.pop()` would do tells me that it may be one of his first JS project).
Now with VIDEO elements, things are more problematic. If you recreate parent-nodes of a VIDEO element, the video will stop playing, or will flicker, or jump back to the beginning.
This isn't necessarily true. In fact, it's not true of Surplus and Radi.js, which is partly why they're much faster than every vdom framework out there.
But they're creating new DOM nodes when values-on-which-the-code-depends change.
I wouldn't frame it as excess complexity, I would frame it as power. My problem is not writing simple hello world apps, it's writing and maintaining very large production applications with 5 other developers. Two years ago I took a week to really learn the tooling stack and it's paid dividends ever since.
The key is good documentation, having an onboard plan for new developers, keeping things stupid simple and stop being bleeding edge. We want our code base to just work, so we can focus more on new features and less on maintenance.
I'm planning on writing a deeper blog post this year about why and how we feel is a good structured simple way to do frontend development. I believe there are several frustrated frontend developers out there that just want to make their life easier but currently all the arguments they find just favor 'React'....
Our whole Javascript codebase is 50k + lines. But the clients only fetch what is needed.
Color me skeptical. React is quite literally the only framework I've ever found to be enjoyable (and maintainable).
This is false or misleading. There is complexity, yes, but those tools abstract it away and simplify everything. Then they have configs with great documentation for when you need to deviate from a default or add a plugin. Next, these things are pre-configured for you. "More complex than it should be" is when you try to reinvent the power of these tools for literally no coherent reason.
Have you tried to build a components library, or have you seen any component libraries that is built on top of a framework that doesn't provide lifecycle hooks for components? Something like https://ant.design/docs/react/introduce or https://developer.microsoft.com/en-us/fabric#/components
They have virtually nothing in common. Web components are essentially just new DOM events, albeit more restricted.
I started this thread by mentioning Surplus, which has no lifecycles because it operates directly on the DOM, and so React's lifecycle callbacks have no use.
"Overcomplication" is in the eye of the beholder. You can't tell your client that his wanting a map in the contact page is "overcomplicating a conceptually simple process". At some point, if a system's restrictions are too strict for the sake of simplicity/beauty/etc, then it ceases to be useful in the real world.
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 library will ensure that the amount of nodes that has to be visited is at a minimum. But that doesn't mean DOM nodes don't get rebuilt from scratch.
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 performanceIf you think so, then there is really nothing to discuss ;)
CSS Grid is for layout, not tabular data. It allows you to move the presentation of your elements around at different breakpoints regardless of your markup structure.
In general I recommend understanding a feature before you spout off nonsense about it.
I disagree. Conceptually, it is exactly how you would do layout in the old days. This still works, btw, it's just considered bad practice.
It's a bit like saying that a kitchen knife is totally unlike a dagger and completely unrelated in every way, because the former is for slicing food and the latter for stabbing.
Horses were never really "vehicles." We just misappropriated them that way, because they just happen to be big and move around and we can sit on them. Cars are purpose-made to be vehicles for humans.
If a designer shows you their wireframes with their grid lines drawn on top of it, I don't think many people would think "ah! so it's like in a science paper when they're printing the raw data from their experimental results in tabular form."
The fact that some web developers consider them alike is a historical accident.
In the following example, I change the button id on click. It's still the same button element after the change:
You're absolutely right that reactive systems are not trivial to implement (let alone implement well), but inefficient DOM recreation under these systems is a symptom of poor granularity in the reactive layer (which may be a problem with the reactivity library just as well as it could be poor usage of its API), rather than a limitation of the rendering library itself.
I'm also worried about debugging accidental dependency triggers. Imagine that suddenly the scroll-state of some div is reset; how do you determine which dependency caused the DOM element to be recreated? With a vdom you avoid the whole problem.
Normally, I would toss in a throw and look at the stack trace. But yes, the stack trace gets noisy as hell.