DEV Community

antonmak1
antonmak1

Posted on • Edited on

Why is reactivity without VDOM (with real DOM) better than reactivity with VDOM?

Hello! In this article, I would like to share my thoughts on why a virtual DOM can be avoided when creating reactivity today. I've been working with all this for about a year and a half now, creating the Cample.js framework, and I have some thoughts on this.

Perhaps I'm wrong. Therefore, if it’s not difficult for you, you can correct me in the comments.

From the moment I start talking about the differences, I would like to write about what “reactivity” actually is.

Reactivity is when a view is tied to data in such a way that changing the data causes a change in the view itself.

A simple example of reactivity can be implemented using Proxy, when we write some information to an object and, depending on this, the function that we described will be called.

const obj1 = {
  value: "Text",
};

const el = document.createElement("div");

const updateView = (value) => {
  el.textContent = value;
};

updateView(obj1.value);

const proxy = new Proxy(obj1, {
  set: (target, prop, value) => {
    if (prop === "value") {
      updateView(value);
      target[prop] = value;
    }
    return true;
  },
});

console.log(el.innerHTML);

proxy.value = "Text 1";

console.log(el.innerHTML);
Enter fullscreen mode Exit fullscreen mode

You can implement this action without a proxy, but in general this example shows how it all works. You can add a comparison with the old value (oldValue !== value), but then you can start looking for the topic of deep comparison of variables, and the like, and then the example will be much more complicated.

In general, the important idea is that we changed the value of the object, the wrapper intercepted the event and called the function that we described.

Now, let's imagine that we are making a website. The site has its own DOM tree of elements.

Image description

And for it in javascript we can create an object in which the real DOM will be reflected through nesting. When data changes, we can update this object first, and then update the DOM itself, but there is a nuance.

const a = {
 tag:"DIV",
 childNodes:[...],
 listeners:[...],
 atrributes:[...]
}
Enter fullscreen mode Exit fullscreen mode

The nuance is that it can be slow and somewhat pointless. Essentially, what is meant by “updating the view”? If we list the actions that are done, then in fact this is the replacement of one node with another, its removal and addition. Updating the text of the node and updating the attributes of the node, as well as the work of event handlers with new data and, in fact, that’s all. What else is needed to update the DOM on a website? If all the work goes through the framework and no third-party libraries suddenly add their nodes to the DOM, then, in fact, this will be enough.

You don't have to work with the virtual DOM. Why is there a need to update it first, and then suddenly update the real DOM under such conditions? Isn't it possible to immediately update the real DOM just by comparing the data?

Well, actually you can. And for a long time now, like 4 or 5 years ago, there have already been a bunch of frameworks or libraries that do “without” working with the virtual DOM.

No, that is, they do not work with it in the established sense, when tags are compared - no. The point is different. Now, there is one clear pattern for almost everyone - this is creating a component template.

So, this is where the virtual DOM is used. And he is the coolest one there, because he helps to find the fastest way to assemble DOM nodes.

In short, imagine this situation. You have a line containing HTML code, and you need to create 1000 DOM nodes according to its pattern.

const stringHTML = "<div>123</div>";
Enter fullscreen mode Exit fullscreen mode

The easiest way to do this is to process this string once into an node and clone it continuously. That is, just like with class, when you create 1000 new instances of the class.

This is roughly how it will look in code.

const doc = new DOMParser().parseFromString(stringHTML, 'text/html');
const el = doc.getElementsByTagName("div")[0];
const newEl = document.createElement("div");
for(let i = 0; i < 1000; i++) newEl.appendChild(el.cloneNode(true));
Enter fullscreen mode Exit fullscreen mode

And that's where the virtual DOM comes in handy for creating the best template! Of course, you can do it this way, but it will be terribly slow, because the algorithm for creating refs to DOM nodes will work like a simple iteration to the desired one:

for(let el of document.querySelectorAll("*")){...}
Enter fullscreen mode Exit fullscreen mode

That is, to summarize, the main message is that the virtual DOM is a necessary thing, but not always necessary in reactivity. Today, textContent and setAttribute can work quite well without updating first the virtual DOM, and then the real one.

Yes, there is very fast reactivity with the virtual DOM, but the very fact that you update the object first, and only then the DOM, is not always cool, because this is another action in the code that is not always necessary, unless you go through the nodes that have been added somewhere outside the framework or library.

In general, we can also mention the existence of "keyed technology", when the data key is tied to nodes, but in this sense, the difference in reactivity is not related at all, because there is a different concept.

Today, to update the DOM at a minimum, just an object with the following properties is enough:

  1. Link to DOM node
  2. References to those child nodes contained in node
  3. Old values of updated data in DOM
  4. Key (Optional)

And, in principle, this is enough. You don't need a tag, some extra objects, etc. It is clear that this is all superficial, but, in general, it is somehow so.

Thank you all very much for reading the article!

Top comments (0)