If you can control your app’s structure, it primarily adds significant increases in the RAM and CPU required for your app and slows load time because you are using a huge amount of JavaScript to emulate the carefully tuned C++ code built in to the browser. If you notice, most of the benchmarks from when React launched claiming performance wins were compared to heavyweight frameworks or complex jQuery plug-in combinations where a single user interaction might trigger cascading updates forcing the browser to rerender things which didn’t change along or to reflow multiple times in cascading update-measure-update chains. Pure DOM implementations were always faster, often by multiple orders of magnitude and once you could drop IE6, and then IE11, the DOM APIs and CSS were rich enough that much of the library code is now a net negative as well (e.g. people used to use complex code trying to build layouts which CSS grids solved).
It can also simplify some stuff surrounding event handling (but that's not it's main goal I think)
So people wrote various ways to defer/batch/denounce updates.
Virtual DOM is a general solution/implementation. It's not the only one, but I think you always need at least a tiny runtime to avoid too much DOM access (ie Svelte, Solid JS are fairly minimal)
Unless you use lit-html, which has a very efficient diffing algorithm that only updates the nodes that have changed.
https://lit.dev/docs/libraries/standalone-templates/
"lit-html lets you write HTML templates in JavaScript using template literals with embedded JavaScript expressions. lit-html identifies the static and dynamic parts of your templates so it can efficiently update just the changed portions."
> efficiently update just the changed portions
Since actually applying each change to the real DOM is too slow, the only way to efficiently update is to batch changes and then apply the delta to the actual DOM.
That means we need to keep track of some state, namely the previously applied state and the current goal state, which you then compare.
Now, you may have noticed that we've just independently invented the concept of diffing. And the extra state that needed to be tracked can be given a spiffy name, like "virtual DOM", since it's like the DOM, but not the real thing.
So, I'm quite unconvinced by Lit-html's claim that they are able to efficiently mutate the DOM without using a vDOM anywhere.
Either their method is not efficient (for example it falls over for rapid updates), or there is a data structure under the hood that is analogous to a vDOM, even if they prefer to give that data structure a different name.
https://krausest.github.io/js-framework-benchmark/current.ht...
1. It leans on support for the <template> element from browsers to hold fragments of HTML
2. For most use cases, it has a more restricted programming model compared to React or other vdom libraries, because templates are not allowed to change the shape (the tree structure of nodes) of the templated DOM.
3. For some cases where you want it to act more like React (for example, dynamically picking an HTML tag) you must use other mechanisms such as unsafeStatic. The docs explicitly tell you this immediately triggers a re-render = slow.
So I guess that answered my own curiosity: the vDOM is mostly replaced by a collection of static templates that don't need to be diffed, because the onus is on the dev to write DOM fragments where the tree does not change.
This is a more restrictive model that what React gives you, where you can generate any fragment tree, including ones with different shapes, entirely programatically.
If you do want the tree's shape to change, then lit-html isn't promising you good performance. You'll need to use methods like unsafeStatic which are slow. All in all, this is pushing more work off onto the developer.
Is this a good tradeoff? I think for most websites you can probably work within Lit's programming model. But the benchmarks you yourself linked points to many, many vDOM libraries that are about as performant as Lit (including React, whose main downside somewhat more memory usage) and has a more convenient React-like programming model.
I disagree about the trade-off in convenience tho, React programming model (especially related to managing state) is quite confusing, verbose and error-prone.
Multiple useless re-renders are the norm.
useEffects inside the body of functions a very poor way to manage the lifecycle of a component and this is way simpler with Lit.
All in all, just for it to be more like React pre-15 I would choose Lit.
People often mistakingly describe the vdom as faster than the dom, this is incorrect. It would be faster than throwing away the whole components dom and rebuilding, so the same templating code building a new dom, rather than a vdom that's then diffed. Hand crafter mutations will be faster than a vdom diff, simply because the computer is doing les work, however much more error prone.
Garbage collection is less efficient, but it is sometimes very difficult to figure out exactly when a piece of memory stops being used, which leads to use-after-free, double-free and memory leak bugs.
Same goes for classical UI approaches. In classical UI, most pieces of state are kept in at least two places, once in code and at least once in the DOM.
For example, in a shopping cart, the total might appear three times, implicitly in the code (as a function that sums the prices of all the items), once as the label of the "open cart" button in the navbar, and once as text in the "your cart" modal, which that button shows or hides. THe cart may be modifiable from different places, the cart modal itself, product pages, product collection pages, order history (when re-ordering recently purchased items) etc.
In the classical approach, you need to make sure that all modifications to the cart accurately change the state in all three places. You also need to ensure that if you remove a product from the cart using the modal and you're currently on a page that lets you order the product in any way, the "remove from cart" button on that page needs to turn back into "add to cart", and there may be hundreds of different such buttons, which the cart modal needs to handle somehow. It is very easy to make mistakes here and have the state in the code (array of products) fall out of sync with what the user sees on the page.
In React, there's just one array of products, one function to calculate the total, and a lot of places in the code that use this array. WHenever the array changes, the pieces of the page that rely on it automatically re-render, while everything else stays the same. There's no way for the UI and the array to fall out of sync, and there's no need to track where the array is being used and where it's being modified.
You'll get better performance with _carefully crafted_ DOM access, but that's easier said than done, especially on a larger applications.
vDOM takes care of the "carefully crafted" part with some trade offs, especially if it also defers rendering and doesn't wccess the DOM on every update.
So yes, it's easier to write declarative UIs with it, but it's also there to address common performance issues with unchecked/eager DOM access. Even if you don't throw away the whole tree and insert a new one, it can be very slow. Just _reading_ from the DOM is slow _and_ everything stops while that's being done too.