Hacker News new | ask | show | jobs
by naasking 3027 days ago
Surplus [1] is the fastest framework in the big JS benchmark challenge, and it too uses the native DOM directly rather than a virtual DOM. Looking forward to seeing Radi.js added to that benchmark suite to really put it to the test.

[1] https://github.com/adamhaile/surplus

2 comments

The problem with not using a virtual DOM is that you can't merge in changes to stateful DOM elements. For example, an INPUT field has state (its value, plus its scroll position). If you regenerate the INPUT field from scratch, you risk losing the state. In contrast, if you use a virtual DOM, the vdom mechanism can merge in any changes for you (the INPUT field will stay the object!)

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.

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 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.

> 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, but they do so for the sake of performance
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.
The element creation part will be triggered if a creation is required. It won't be triggered by a prop update, for instance.

In the following example, I change the button id on click. It's still the same button element after the change:

https://jsfiddle.net/dwn36aw4/8/

what you discribe is basically what https://github.com/google/incremental-dom is all about and in certain cases it probably is as fast as vdom and also less memory heavy.
I don't see why this would be any more difficult without a vdom. For the textarea, either way you're merging two text blobs, and if the vdom can maintain scroll position while doing this, then so can the non-vdom using the same mechanism.
Let me try to explain. Let's say you have a textarea element which has DOM-state (value, scroll-position). And let's say you change the value through your render-framework. With the vdom approach, only the value component of the DOM-state is modified. With the non-vdom approach, you're creating a new textarea element, and set its value. Its scroll-position is lost, unless you keep track of it.

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.

> With the non-vdom approach, you're creating a new textarea element, and set its value.

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.

They're faster because they are not computing DOM (or vdom) nodes when that's not needed.

But they're creating new DOM nodes when values-on-which-the-code-depends change.

What primarily makes surplus fast isn't necessarily that it doesn't use a virtual dom, but that changes are tracked with a data reactivity library reminiscent of knockout called s.js instead. On the DOM side, it has a compiler like svelte that generates JIT friendly expressions, e.g. el.className = val instead of el[prop] = val.
> but that changes are tracked with a data reactivity library reminiscent of knockout called s.js instead

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.

Lifecycles are only needed because of the vdom, it's not a desirable "feature" since it overcomplicates a conceptually simple process.
Lifecycles has nothing to do with vdom. Even web components has lifecycle hooks https://developer.mozilla.org/en-US/docs/Web/Web_Components/...

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

The term "lifecycle" in web components has a completely different meaning compared to its use in vdom implementations. Compare the semantics of "lifecycle" events in web components against React: https://reactjs.org/docs/react-component.html

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.

> They have virtually nothing in common.

If you think so, then there is really nothing to discuss ;)

Lifecycles are important if you need to do some arbitrary DOM manipulation upon mounting an element to the DOM tree. For example, you may want to render a google map when navigating to some route.

"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.

Does S.js keep track of dependencies that are no longer needed? Is there some sort of garbage-collection implemented?