Plug: I help develop
million
: <1kb virtual DOM - it's fast!
Introduction
The Virtual DOM was initially pioneered by the React authors on the basis of making delarative JavaScript patterns performant - but how? To understand this, we need to quickly review how traditional DOM manipulation works.
Generally speaking, the easiest way of changing the DOM ("Modifying the HTML") is to mutate the innerHTML
property on an element. For example, if I want to add a div
element in the document body, I could do something like this:
document.body.innerHTML = '<div>Hello World!</div>';
// <body> now has a <div>Hello World!</div> child.
This seems to be computationally performant, but it really isn't. While the action of reassignment is computationally performant, the DOM repaint ("Updating what the user sees") is not. This is because innerHTML
needs to parse DOM nodes from a string, preprocess, and append it, resulting in less-than-optimal performance. The issues with performance are increasingly noticable when there are more children/attributes and when the interval of mutation is shorter.
So, how is this issue fixed? Well, instead, we do pinpoint changes to the DOM. For example, this solution would be almost 10x faster than the innerHTML
solution.
const div = document.createElement('div');
div.textContent = 'Hello World!';
document.body.appendChild(div);
While this is simple enough, once you start performing continous mutations, more complexity arises. This is why the virtual DOM was created - to allow you to write declarative content (like the string in the innerHTML
example) while harnessing performance by making only pinpoint changes to the DOM.
Virtual DOM
The virtual DOM is a tree of virtual nodes that represents what the DOM looks like. virtual nodes are light, stateless, and are JavaScript objects that only contain necessary fields. virtual nodes can be assembled into trees, and "diffed" to make pinpoint changes to the DOM.
While this is efficient, it has some caveats. Notably, diffing is not computationally free. Traversing the trees have O(n^3)
time complexity, meaning the more children, the longer the time it will take to perform the action. To solve this, Million was created.
Read this article if you don't understand what the Virtual DOM is.
Million
Million provides five major improvements: granular patching, fewer iterative passes, fast text interpolation, keyed virtual nodes, compiler flags.
- Granular patching: Instead of just replacing the entire element when there is a difference in props or children, only the necessary props are changed.
- Fewer iterative passes: Million attempts to reduce the amount of passes during diffing, allowing for better time and space complexity.
-
Fast text interpolation: Instead of replacing text nodes with DOM methods, Million uses compiler flags to set the
textContent
of elements to boost performance. - Keyed virtual elements: This allows for the patching algorithm to skip nodes if the new virtual element key is the same as the old one, minimizing the amount of unnecessary work.
- Compiler Flags: This allows for the patching algorithm to skip condition branches, meaning less work is done.
Thanks for reading! Drop a star to Million or follow/react to this article for more Virtual DOM content!
Top comments (52)
Yep, decade old misconceptions recycled. You may want to consider: svelte.dev/blog/virtual-dom-is-pur...
Yeah, innerHTML is what people used to try before the first js frameworks came to be. Nobody does that anymore.
Just think that virtual dom changes do need to be eventually applied. How does that happen? Hmm.... I wonder if it has anything to do with Svelte being overall faster than React.
Bottom line is: virtual dom isn't necessary at all. It's necessary in React because of the way React defines component-managed state and state transitions but that's about it.
While I agree with you that Virtual DOM isn't necessary for most use cases - my original intention was to phrase it as "Why is the Virtual DOM necessary in most cases." The argument that the .innerHTML is just a scapegoat and "nobody uses it anymore" is false, take jQuery users for example. I don't even want to point out the transgressions made with $(el).html(). However, that shouldn't be the discussion here, rather the example posed in the article is just that, it's not an argument point but rather an example to demostrate why the Virtual DOM was created in the first place.
Just claiming "Svelte is faster than React" is naive. Personally, I use Svelte more than React, which was why I created Million in the first place - to allow the compiler to generate as much static content as possible while minimizing runtime. This allows for significantly less size (as you're just generating a data structure) vs basically imperative code, as well as pretty good performance overall.
While I agree with many of your points, I think you could consider doing some research into this field - there's a ton of interesting complexity and context to discover and learn.
What exactly is "most cases" because if you want to argue as you did in the comment (which makes more sense than the article)?
Virtual dom is better than inner html. Jquery users use innerHTML but... that's not a problem unless they also replicate a react-like state management which would need to track a certain number of changes that makes that impractical.
But then a better choice is to use Virtual DOM. But Virtual DOM is an pute angular concept, so you're saying that React is necessary.
Saying that virtual DOM is necessary in most cases means that there's really no other way to achieve said cases without it. Which is technically not true.
I rephrased the article title to be "Why Virtual DOM"
Honestly, I'm not sure I completely understand what you're saying here, so I'll try my best to answer. While writing straight performant jQuery will be faster, it doesn't provide a declarative experience developers use React for in the first place. Additionally, Virtual DOM is not a pure Angular concept? I think you misunderstand.
Most of your concerns should be addressed with the new article title.
Perhaps I gave a bit the wrong impression. I don't quite expect 'adjustments', merely a discussion on options available for a given goal.
What I'm saying is that there are different options available, which by default means that Virtual DOM will never be the "necessary" option. Something becomes the necessary way when there's either no alternative or the alternatives are so bad they're not worth considering.
As a side note, certainly jQuery's dev experience will never be a match to pretty much anything else out there today, but that's a different kind of concern.
What I'm saying is that Virtual DOM is but one way (and also that Virtual DOM is not a Angular concept, it's a React one).
What are the ways?
So Virtual DOM is just one of at least three efficient options on managing DOM changes, so "why use Virtual DOM" could reasonably be expected to argue why Virtual DOM is a better option that any of jQuery's way (which is only as efficient to the extent that a developer is a master or algorithms), Angular's way or Svelte's way.
Thanks, this explaination is much more clear. I agree with pretty much all your points, but I think you are approaching this the wrong way. I think you are segregating the options into their own categories, and comparing them just like that. This is a valid comparison, but not how Million approaches it. Million allows for more fluiditiy between the categories, as it takes the "compiler-time" optimization you mentioned + Virtual DOM together, bringing the benefit of both worlds.
I hope this brings more context
what advantage does MillionJS bring over Svelte? Svelte is both declarative and fast, which seem to be your main concerns
It's very difficult to compare Million to Svelte, as Million is a VDOM engine, and Svelte is a JavaScript framework. It's like comparing apples to oranges.
but you wouldn't need MillionJS if you used a framework like Svelte. So what advantages do runtime-based frameworks (like VDOM frameworks) have over compile-time frameworks (like Svelte)?
VDOM is not a framework. Generally speaking VDOM is not used by the average user, rather it's a engine to build frameworks off of.
I understand that, but by VDOM i meant virtual-dom frameworks like React and Vue. There are also frameworks like Angular which don't use virtual dom but are still runtime frameworks.
The reason why I asked is because it looks like MillionJS is an engine that people can use to build more virtual-dom frameworks. But if compile-time frameworks like Svelte are the future, why would one want to build another runtime framework?
It's very difficult to broadly claim that Svelte is the future, but I 100% agree that compile-time frameworks are the future. Million.js is like the intersection between VDOM + compile-time, as it can be further optimized through compile time and is aware of the compiler. Virtual DOM is super, super powerful in terms of declarative programming, and combining the two can achieve the best of both worlds.
well I guess the way I see it, virtual doms wont be needed when we move to compile time frameworks. The name "virtual dom" comes from the fact that it's an in-memory representation of the DOM. But this just adds unnecessary overhead, since compile-time frameworks compile the code such that operations are done on the DOM directly.
Ultimately, the new mindset is that the programmer writes their declarative code, and the framework/compiler decides everything between the code and the output. If direct DOM manipulations are optimal, the framework will do that. If a virtual dom is optimal, then the framework will output a virtual dom. Right now MillionJS feels like it's working with outdated technology. I think if MillionJS wants to be ready for the future, it needs to provide a more general framework for static analysis.
Generating straight imperative code has many downsides. For example, there would be an increase in the generated to source ratio, increasing bundle size, vs. just generating a data structure which would save a lot on bundle size. Even then, Million proves than in the benchmarks, it's able to match up against imperative DOM manipulations, thanks for shortcutting. Virtual DOM is here to stay, but it needs to evolve to mesh with the new paradigms.
Static analysis is great, but there are already so, so many SSG/SSR frameworks/integrations out there, namely 11ty, Astro, etc. These libraries have found it's very hard to achieve zero runtime (unless your page literally has zero interactivity). Million.js attempts to address this outdated technology and shift the VDOM paradigm to the modern age.
Svelte has already calculated the inflection point where the bundle size becomes larger than react, and it's much larger than most applications will reach, see here. Even so, as I said a smart static analysis framework would be able to generate a virtual dom if it were optimal (but I guess Svelte is not there yet).
but for now, I think virtual doms are starting to fall out of use. They may prove useful again in the future but right now it seems like frameworks like Svelte are just all-around better than virtual dom frameworks (aside from the maturity of React and Vue I guess). Though I am curious to see the benchmarks of Svelte vs MillionJS in some real life application.
Also note that SSR and SSG are not really static analysis technologies. They don't analyze the code, they just runs the code and generate the initial DOM tree. Static program analysis is in pretty early stages for web dev and Svelte is the first framework to utilize it and take off, at least from what I've seen (though I guess bundlers like Webpack sort of count as well, though the analysis is not as complex as Svelte's)
Anyways, I don't mean to disparage your work, I think it's an impressive and interesting project (otherwise I wouldn't have looked into it). I just think it has a few directions it needs to go if it wants to keep pace with modern developments.
The inflection point project you referenced looks really interesting - but it only compares React 16 and Svelte, which could differ from application to application. I could be misinterpreting the readme, but in my opinion I am doubtful of the results when applied to different scenarios, as there is just so much variablity possible that could change the inflection point(s).
It's very difficult to claim that Virtual DOM is falling out of use. While some popular + successful libraries like Svelte/Solid have outright rejected the Virtual DOM, a significant portion of libraries and newly created libraries will continue to use Virtual DOM. It's just so difficult to compare libraries because of subjectivity, and claiming that certain libraries are "all around better" than others is an overgeneralization in my opinion. I think a MillionJS and Svelte benchmark could be interesting, but they are so different that it would only be accurate for an indirect comparison (through a compiler library built to optimize for Million).
I think SSR and SSG do some static analysis. For example, astro removes any unnecessary code, some SSR/SSG do static prop, component and vnode hoisting (might be bundlers, I'm not sure), but I agree it's not to the extent of the application of static analysis in other programming langs. I think a possibile difficulty Svelte devs might be facing is just that flow analysis is hard (e.g. prepack), and it'll take time for this technology to develop.
a lot of the stuff you mentioned is either runtime optimizations or bundling (which is static analysis, but it's really just tracing import statements to find which code is used and which isn't). And SSR just runs the code to generate a DOM tree, it's not analyzing the code or generating code . Real static analysis is difficult, as you pointed out. But that's why Svelte is making such big waves. And prepack looks to be going that direction too. It's a huge field with a lot of opportunity.
But yeah ultimately it seems we disagree on what role VDOMs will have in the future, I guess we'll just have to see :)
Ah yeah, I was so hyped for the prepack project but I'm kinda bummed out facebook basically just dropped the project
yes its about react. But ...
svelte.dev/blog/virtual-dom-is-pur...
If you read the article Lars sent, make sure to read and understand the entire article - Just reading the title is a misconception without context. Virtual DOM is generally efficient if you only have runtime, and was invented in a time when "compiler-frameworks" like Svelte didn't exist.
I agree with Aiden. Svelte is a compiler, it is very beautiful but works in a very different way. If I wanted almost zero bundle size on my personal website that needs small JS based widgets, I might go for Million... BUT!
The API provided by Million reminds me of the 2 KB library called, "re:dom". Creating a todo app in re:dom is quite something! Compared with Vue, Svelte or React, in re:dom, I need to update state and then manually pinpoint the element I want to update.
Such level of micro-management makes my life harder and I won't mind using an alternative even if it adds a few more kilobytes to the bundle. Developer experience is also very important. P.S, I've used re:dom in some of my projects to make DOM elements creation a breeze.
So, could you please include a todo app in your documentation? Let's see how different it is and how easy it would be compared to re:dom given that there is a fast effective patch method in Million?
I actually haven't heard of RE:DOM, but I just checked it out. It seems from (my very, very brief skim) that it provides a little bit of abstractions over what the DOM already is. Personally, I wouldn't call this declarative web programming, but I also probably do not understand this library enough.
Because it looks to seem like it only provides a minimal layer of abstractions, it's going to be fast - if you write basically native imperative DOM code, it's always going to be fast.
Regarding the todo example, there is a Hello World example in the README + docs, but no todo example. It will probably be easier to manage state in the libraries you mentioned than Million, but that's not the use case of Million. You can imagine Million as the "build target for a compiler." By itself, it's just a Virtual DOM, and other developers can build their own libraries, paradigms, concepts over it (with React, Vue, Svelte, other syntaxes)
Possibly once there is an ecosystem or libraries built around it, more todo examples will pop up and we can see something cool out of this. Thanks for the feedback!
Ooh, thanks for the clarification. I also looked into Lucia, it's a really fun tool. I love how I don't have to write any JavaScript and this 3 KB library can handle the reactivity. Honestly, you must be a genius. No doubt you will end up creating a tool that millions of us are going to use.
Haha, thanks for the compliment, I'm happy to help.
not only overhead it's not good for SEO
Which is why raw runtime VDOM is never used for most real websites - SSR is needed if you need SEO. Anything rendered with JavaScript is "not good for SEO"
The DOM can be used both very effective and very ineffective. The same is true for the virtual DOM, but libraries like react make it a bit easier for less experienced developers to use it effectively - and that's a good thing.
However, we had handcrafted DOM manipulations more than a decade ago that were easily faster than react - because they only made the necessary changes and reused instantiated nodes without any intensive reconciliation.
Libraries like Svelte and SolidJS help to have almost the same performance without the hassle and easily surpass react and other virtual DOM libraries in terms of performance.
I agree with a lot of these points in this statement - but I think you are generalizing here. While manually writing direct, imperative DOM manipulations is always going to be faster than any library you use out there, that's not really how we do web development today in real production use cases. React was created to make webdev paradigms delarative, and inventing the virtual DOM is just a means to an end.
I actually checked out SolidJS the other day, I found the concept very intruiging and the beta of SolidJS actually inspired me to create something like this in the first place. I loved how they optimized their own architecture, so I took some of the broad design principles and applied it to my own project.
However, I partially disagree with your generalization that Svelte and SolidJS is better for development experience and always has better performance. Development experience is subjective, it's difficult to objectively define whether something is "worth the hassle" or not. While React may be overall slower that Svelte and SolidJS, using React is a scapegoat in itself. MillionJS, compared to React's internal Virtual DOM engine is very different, but suffers from the same concequences as all Virtual DOM does. This is why MillionJS has several compiler-aware optimizations, so that in the future, Svelte + React mixes can be created.
If performance is your highest priority and the page is rather simple, it is still a sensible choice to develop exactly like this β and I actually did that ~ 3 years ago. Unfortunately, we had a priority shift in the meantime, so I cannot show you the result anymore, but it easily outperformed anything we could have done with react.
I would partially disagree, too, because I made no such statement. I merely remarked that they save you the hassle of hand-optimizing DOM manipulations while allowing for similar performance.
React is a compromise β and as I already said, that can be a good thing, too, depending on what you intend to do.
I see, personally I would prefer the best performance, but to each their own. I'd like to see the example if you ever get to find it!
I think I misinterpreted your statement there, I thought you were talking about React when you remarked about the "hassle" part.
It can also be noted that using any technology is a comprimise - for example, using DOM nodes generally constrains you to imperative programming, and Svelte constrains you to it's own syntax, while providing extreme performance. I think a better phrasing could be "a means to an end" to provide declarative programming.
You're right.
Every library and even using no library is a compromise between time, performance and features (maintenance costs time, so a library that makes it easier will save time).
I'm currently using react at work and it's a rather good compromise, because we work with 4 teams of varying experience on a very complex application.
But back to the original point: a virtual DOM is not a necessity, but merely one way to solve a certain set of problems β and in some (but not all) cases, other solutions might be a better fit.
That's a good way to put it, I just rephrased the title of the article so it's a bit more vague, but no longer claims that it's a necessity. Thank you!
Did you look at how they set up the tests?
Could be me misreading it but it seems like they deliberately made the vanilla JS test dumb so that it keeps overwriting the HTML (copy existing, add new item, then replace all the HTML with the new HTML) instead of adding to the HTML.
That isnβt how anyone would do things in the real world. (Or if they did they wouldnβt be adding 1000000 DOM elements)
For the benchmarks, there is a distinction between the "baseline" (or most optimized imperative score) and "vanilla" (or what developers naively do often when using vanilla.)
It can be easily argued that benchmarks generally have no basis in real world instances - and this is very true! Generally, you should take benchmarks with a grain of salt, which is why I provided some points where optimizations were performed. In my opinion, providing benchmarks is better than making an unsubstantiated claim of "it's just fast," whether you take the benchmarks as 100% accurate for your use case or not.
Good point, well made! It just always feels a little disingenuous when in a graph form as it leads to the misnomer βthe virtual DOM is fasterβ and that gets touted about all the time! π
Thanks! Yeah, this may be my fault, I tried to bring as much nuance into the article as possible to communicate that there is more to it, but it's often difficult to get readers to understand. Generally, readers will just skim over and get the big ideas, which is an issue and proliferates the misnomer you brought up. Context always matters!
This article was very informative. I really learned a few things about Virtual DOM! Could you also please include the best and worst case time and space complexity for the algorithms used in Million? Thank you!
I'm not completely sure on the time and space complexity for a virtual dom tree, but I believe from what I've dug up online it's worst at O(n^3)
I'm seeing that svelte is referenced in the comments a lot and seems dismissed as a straw-man argument since the because of the underlying nature of being a compiler (rather than a framework in the react/vue sense).
While Svelte may be a compiler, the common performance bottleneck with frameworks seems to be the diffing of a vdom, and while many strive to optimize the diffing, solutions that have done away with vdom completely seem to be performing the better. Have a look at solidjs, which is not a compiler, and offers a react-like api without the virtual DOM.
I disagree with your assesment, at least for my comments, I generally openly embrace Svelte design principles (like compiling). Otherwise, I may say it is difficult to compare Svelte to MIllion, because they are two very different things.
I imagine that generalizing that "Compiler-based frameworks always perform better than VDOM solutions" is untrue. While it may perform better in certain cases, these compiler libs need to handle many pitfalls and caveats of not using VDOM (i.e codegen, mental model, etc).
I think Solid by itself isn't a compiler, but it does take many compilation steps to optimize it's code.
I think there's a misunderstanding here.
My argument I guess can be summed up as "VDOM is not necessarily the Holy Grail, and frameworks that have chosen another route seem to be performing better"
So to clarify. I agree that svelte is a compiler, and that make it not directly comparable, but am offering
Solid
as an example of a framework that doesn't require compilation (It is built using brutally efficient fine-grained reactivity(No Virtual DOM))[dev.to/ryansolid/introducing-the-s...]Of course that's not to take away from the work you've done on million, but rather to clarify that there are other options and hopefully offer some perspective to Why not Virtual DOM
I agree with you that VDOM is not the holy grail. Just to note that I never claimed that VDOM was the holy grail either.
I feel like saying that Solid doesn't require compilation is the same idea as saying React technically doesn't require compilation. You can technically run both in the browser without a build step, but at an extremely degraded user experience.
That perspective would be very interesting to hear! Maybe you could write an article on why not Virtual DOM on devto π
In my experience, vdom performance is related to the algorithm used. Vdom is a tree, a data structure. It can make me do more and better algorithms. It is not possible to use DOM structure only.
And the structure and algorithm used in time slicing, offscreen rendering and these features must be generated by ourselves.
github.com/yisar/fre
Defination of virtual DOM that I follow:
Its memory representation of real DOM.
Why virtual DOM is faster?
Ever heard of movie Inception . That movie is all about how we can spend decades in a dream that's relatively super small i.e. the brain works faster that reality. Likewise DOM is the reality and Virtual DOM as name suggests is memory.
You'll getit once you've seen the movie.
I'm unsure what this means but if that analogy helps you understand the Virtual DOM, the more power to you.
Just helps me visualise better but that's just me.
Svelte, Solid js and lit all of them provide declarative APIs without the overhead of a virtual DOM.
Solid js in particular is the fastest and one of the leanest frameworks out there having a declarative api.
solidjs.com/
lit.dev/
svelte.dev/
In my opinion, the value of VDOM based solutions is maturity and ecosystem around them, instead of performance or smaller bundle size.
DML
I just published a new framework called DML, which is all about Direct DOM manipulation. Here you do not access the DOM, you just create it. So, all operations are directly changing the DOM, ui-elements are closely tied to (local) javascript variables.
Is it slow? Does it make unnecessary screen updates? Definitively not! ItΒ΄s blazing fast! I was really amazed about the speed and the really smooth operations in any possible situation.
Modern browsers like Chrome or Firefox do a great job to hide unnecessary screen updates, so maybe a "virtual DOM" does a job that is already covered by the browser engine. Maybe this is different if you get all your updates via AJAX, as this really slows down everything. But if content is created and changed inside the browser, it seem that things have changed over the last years...
Best regards, Eckehard