Hacker News new | past | comments | ask | show | jobs | submit login
Cample.js: Reactivity without virtual DOM (github.com/camplejs)
50 points by Cample on Feb 10, 2023 | hide | past | favorite | 92 comments



There are at least two reasonably popular frameworks that offer reactivity without virtual DOM: Svelte and Solid. And both of them are way more ergonomic than what is presented here.

I believe VDOM-based approaches have one decisive advantage — the VDOM structure can be created by any means convenient to the programmer. To me at least, this offers more flexibility and modularity when organising code: you can create as many functions as you need to structure and reuse VDOM construction. This is something I run into with Svelte, which forces me to make a new component for every bit of code I try to isolate. Solid is a bit better — and I really like their idea of "vanishing components" — but you have to follow it's fairly strict rules (e.g. no props destructuring) or things will get weird.


By and large having no VDOM frees you from having to structure things in an unergonomic way, for performance or otherwise, whatever limitations Svelte and Solid have in this regard are self-inflicted. For example my framework allows you to destructure props just fine, still no VDOM (https://github.com/vobyjs/voby).


You can do the same with any system that treats templates as expressions that return a value (the big innovation of React, IMO), it doesn't require vdom.

In Lit, we do this with tagged standard template literal expressions that return what we call a TemplateResult. You can compute, store, abstract over those however you want - they're just JS values. This indeed gives programers a ton of expressive power.


Sure, that's the most straightforward way to approach these things, but doesn't this mean your elements don't have persistent identity between two renders? Tracking elements between renders is what allows animations and other nice things. So it's not exactly the same.


No, it's better. Retaining identity is _easier_ with such a system.

You mark in the DOM where the expressions are, and on updates instead of a vdom diff you check if the template rendering to a DOM location is the same as before. If it is then you just update the expressions. All the unchanged elements stay the same, and sine you don't traverse the DOM or a VDOM, the update is much faster.


People like to hate on the VDOM, but here's some food for thought:

- VDOM is data-driven without side-effects

- VDOM does not actually require JSX or build steps

- VDOM decouples you from the real dom, which has allowed things like react-native to "just work" the way react web works.

- VDOM means you can make test assertions without having to load up a "browser-like" environment

- JSON is a pretty easy to reason-about data structure, which makes debugging fairly easy

I'm not saying the virtual dom is the best answer, but I guess my point is that it's not really an issue of speed any more. If I recall my internet history, React came at a time when browser DOM standardization was still pretty wonky, so the virtual dom did provide a boost in manipulating the data that would translate to DOM mutations. Now that we're basically all running Chrome or Firefox under the hood (give or take webkit safari), I'm not sure that's the main drive any more, but the other benefits still stand, and that's why things like react are still using the virtual dom.

Anyway, cool library. I'm curious if it provides something that is actually different from other libraries I've used, in terms of interface or flow, and I might find some time to check it out soon. Good job!


The bottom half of this list doesnt seem remarkable to me. Smaller DOMs like Deno.DOM[1] or jsdom also can do that: we dont need a vdom to do that.

[1] https://deno.land/manual@v1.25.4/jsx_dom/deno_dom


Of course you don't need the vdom for testing, but it's typically a little lighter weight since the implementation of the vdom would already be bundled in. That said people typically reach for some special vdom assertion library anyway.


First off, I am always happy to see people trying out new things because it creates innovation. Congratulations on launching!

That said, the big feature here seems to be what the headline says: no virtual DOM. As a user of frameworks, I don't care much about implementation details unless they result in benefits like performance improvements. Does this perform better than React and/or Vue? Or is there some other benefit to it?


Things like Solid (https://www.solidjs.com/) also have no virtual DOM, and the improvements are in higher ceiling for performance, lower memory usage, simpler DX (components are not re-executed, there aren't any dependency arrays anywhere), easy high performance (no useRef this and useRef that to make things fast, no useCallback, no React.memo, these things are just obsolete).


I wouldn’t call it a new thing. We began without virtual DOM and then Facebook sold us the vDOM as the ultimate way to go. Which svelte clearly shows, by removing the unnecessary layer for the vdom it’s much faster and is still reactive.


Or there is Inferno which is VDOM based and outperforms Svelte on most benchmarks. The whole narrative of "VDOM is pure overhead" doesn't hold out in practice, and I suspect really just an excuse to attack React and promote Svelte.

https://krausest.github.io/js-framework-benchmark/2023/table...


Outperforms? Solid, svelte and inferno all look to have very similar numbers in that table unless I’m terribly misreading it. So similar, that I think which way it goes is possibly more dependent on your benchmark environment than any difference between them.


According to that, solid won in the majority of metrics. And what about build size?


Svelte has largest component size compared to other frameworks such as Vue, React and Solid, so it quickly grows, even on a small webapp. Because Inferno is VDOM, I'm assuming it has a smaller component size compared to Solid.


They did this while introducing a lot of compiler magic and automatic dependency discovery that not always works. I’m not sure I’m sold on it. Code you write is not code you execute.


You're correct, it's not new.

Still, the question remains, what's the improvement over Svelte or SolidJS which doesn't use VDOM as well ?


Solid too. But yes Svelte seems to have been the current thing for a few years now.


Lit also does fine without VDOM and has good performance characteristics.


RiotJS as well.


Yeah, it does have significant performance improvements, because diffing the Virtual Dom adds heavily to computation costs if you aren't being extra careful or as the application grows to real-world size, especially in mobile.

No VDom is always faster than having a VDom.

But I do agree with your point 100% that it should tell you right away about it on the README what that means, and the significance behind the claim.


No, it's not always faster.

IIRC in a recent stream the author of Solid showed that it's hard to beat a proper VDOM implementation when you have 1000s of elements.

And most naive non-VDOM implementations will probably lose to a good VDOM implementation.


That’s true, but you can say the same thing about reactivity: not having it is always faster than having it. These are not zero-cost abstractions; we use them because the alternative tends to be less performant at scale, but technically you can always beat them by going in and manually updating the DOM yourself.


> because diffing the Virtual DOM adds heavily to computation costs if you aren't being extra careful or as the application grows to real-world size, especially in mobile.

Is there some recent study showing evidence or analysis of this? You're saying the main root cause of poor performance is the diff burning up CPUs?

I always assumed applications run slowly because people are overusing global (redux) state and every state change is subscribed to by like 10 different components. And people making 5 network calls before their component meaningfully renders.


As ever, it depends on your use case. If you're rendering 20 TODO items then who cares?

If you're rendering thousands of items on an SVG data visualisation, sure it matters.

You can tell it matters because otherwise React wouldn't have implemented escape valves to improve performance, like shouldComponentUpdate.

You can get around this by using persistent data structures with lenses, which is how Elm works. Then it's just reference equality checks as you go down the tree, which is inherently faster.


> If you're rendering thousands of items on an SVG data visualisation, sure [virtual dom] matters.

I am not educated on the no-virtual-DOM-hype.

What actually costs more here:

(1) the diff and vdom algorithm or

(2) the actual DOM calls?

I always thought it was (2)... is this right?


Yes was going to say exactly this, tell my updates are faster or the library is smaller but just saying we built it without virtual DOM doesn’t explain the trade offs in this approach.


How is it different from svlete and solidjs? both are no-VDom approaches.


Can someone remind me why virtual DOM was/is desirable in the first place and why updating the DOM directly is desirable now?

If I understand correctly react elements are created in memory, and only upon "render" it is turned into the actual DOM. During render in react, it does the tree diffing and state management. Supposedly manipulating the actual DOM directly is "heavy" hence delay/pruning the virtual DOM tree first then rendering would be beneficial? Then why is it working with DOM directly is desirable? And am I right to assume that "without virtual DOM" means work with DOM directly? Someone in the comment mention that Svelte is without vDOM already. Is there some design document that I can refer to, like the reconciliation engine used in react https://github.com/acdlite/react-fiber-architecture


I don't know why VirtualDOM is suddenly getting a bad rep. The reason you often want something like a VirtualDOM is so that you can "pre-process" your DOM updates in-memory so that the actual updates, which are computationally expensive for the browser, can be done more efficiently.

I suspect, but this is just my personal conjecture, that the reason VirtualDOM is suddenly falling out of favour is a reaction against very bloated JavaScript applications and the complexity that underlies working with current popular frameworks and libraries. Some are starting to question whether we are working with solutions to actual problems faced or whether we've adopted approaches that were intended to solve a specific problem faced by some but are inefficient for simpler applications.

As always, be an engineer. Consider all relevant factors before choosing a set of tools or marrying yourself to one technology vs another.


I feel like I live in the Twilight Zone because to me data visualization with a VDOM seems much smoother than without like in Svelte even though data visualization is supposed to be a strong point of Svelte.

As someone who has only really got up to speed with React in the past year, I can't understand why people hate on React. Much of the hate has to be residual hate for Meta.

Svelte to me seems like a mess. $:variables seems like an absolutely terrible idea. Maybe it is a good idea if you are a genius but I assure you I am not and can do absolutely confusing and crazy things with $:variables.


> As someone who has only really got up to speed with React in the past year, I can't understand why people hate on React.

I'm not sure where the people you are hearing from are coming from, but I can tell you why I personally hate on React.

It has nothing to do with VDOM or its underlying tech, it is its design.

React gets treated as if it's a framework but is just a view library that gives you the ability to create components.

However, even that ability is rather lacking compared to a framework like Angular. Now, people hate on Angular too for various reasons but to stay on topic I won't digress into that.

Things I'm missing OUT OF THE BOX (yes I know there are 12 billion libraries "for that" that came out this week alone) with React are:

- View encapsulation (styles bound to components)

- A templating language that lets me separate a component's markup from its JavaScript

- Native typescript

- Inversion of Control / dependency injection framework

- Native routing that lets me configure my routes as metadata instead of having to use components, which ought to be strictly view / presentation only.

Working with inherited React code often reminds me of working with PHP in the 90s. Lots of markup mixed with lots of logical operations written by developers who don't know the first thing about separation of concerns, design patterns or layered architecture but the barrier to entry is low so they picked it up and were able to get stuff done with it despite not having any guidance what-so-ever on how to write maintainable code with a long shelf-life.


I think https://svelte.dev/blog/virtual-dom-is-pure-overhead might give you some answers to your question.

My very rough understanding is: It's nice to be declarative and just re-render everything on every state change. This is impractical to do with actual dom, but maybe works good enough with vDOM and dom diffing. Still, doing vDOM and them actual DOM updates is more work than just doing the DOM updates that are needed. And I guess tools like svelte let you be declarative and only make the necessary DOM updates while skipping the vDOM.


Thanks! This helped lot. Now having taking a gentle look at svelte, I think it is very smart. Instead of diff-ing the virtual component tree, it is maintaining a dependency of statements behind the scene by providing a reactive declaration. Upon each event, the re-run the whole tree of statements, then update the DOM directly with the result of those statements, hence no need heavy manipulation of DOM nor diff-ing of large in memory object. I've only just started to look at svelte though, so could be very wrong. But again thank you so much for you pointer, everything just clicked suddenly.


The reason VDOM was desirable is that React's "View is the function of the State" paradigm has a great developer experience, and VDOM was just the easiest way to implement it without sacrificing performance too much.

Now that we played with the easiest implementation of V = f(S) for a few years, we're moving on to more complex implementations, which have their own benefits. Namely, less overhead and better DX.

Frameworks like Svelte are moving good chunk of state management accidental complexity out of your app code and into the framework itself, where it actually belongs (i.e. where it is essential).


> and VDOM was just the easiest way to implement it without sacrificing performance too much.

Kind of. Virtual DOM nodes are extremely lightweight compared to real DOM nodes, so manipulating a VDOM to determine what updates need to be done to the DOM, instead of doing all interactions/comparisons directly with the DOM, had a big performance boost.


No offense, but this sounds like typical React-cult nonsense.

You're still making the same amount of DOM manipulations in the end. And you don't need VDOM to figure out which manipulations are necessary.

There are some potential optimizations VDOM can allow (e.g. batching/deduping), but there's nothing "lightweight" about it. Lightweight overhead is still overhead.


When we started using React in 2015 it was in one of the official explanations (or semi-official, like a blog post by someone working on it). The page I remember involved comparing the number of attributes each node has in the vDOM vs the real DOM and its legacy attributes, and was about both about memory usage and how it restricts the search space by just not allowing most of those legacy attributes.


It's easy to forget nearly 10 years on, but React came out when Backbone.js with mustache templates was rather common, and React was a LOT easier to manage. The other popular frameworks at the time were Ember and Angular 1, which were quite a bit larger/heavier. By comparison, React could be adopted in a much more incremental fashion. React was super easy to adopt (and it still is, but there's a lot more of an ecosystem around it now which makes it feel much more like a framework).


The author seems to suggest that React is reactive in the sense of “functional reactive programming” (a la RxJS) but React has never been “reactive” at all (see “scheduling” heading on https://reactjs.org/docs/design-principles.html).


> The author seems to suggest that React is reactive

The word "React" never appears in the README.


Personally I don't get why you wouldn't use JSX for such a tool. If you're building JS apps for production you'll invariably use a bundler like Webpack so transpiling JSX to JS is not an issue, and it makes Markup code in JS so much more readable.


The self-fulfilling prophecy "you'll have to have a build step" makes me so sad.


With JS modules it might be possible to just write JS code and include it in your HTML directly, I'm not sure if I'd recommend that though as performance might not be great as of now, and there are still many other caveats. In a few years this might be less of an issue though when HTTP/3 is widely deployed and loading many tiny files instead of one big one won't make that much of a difference.


HTTP/2 already mostly solved that, as I understand it, except for some retransmit issues that can't be fixed until TCP is replaced with QUIC. Right?


For one, JS engines don't support TypeScript natively and it turns out that many teams who build and maintain large, ambitious SPAs think TypeScript is a productivity boost.


Not everyone uses/prefers TypeScript, and not everyone is on a team that builds and maintains a large ambitious SPA. There's room for both frameworks like Next.js and small libraries that work without build steps.


If you don't have a build do you just have a couple giant js files you edit?


No, you can use ES6 modules. They've been natively supported by all browsers for years.


The problem is that this scales poorly as the depth of your module tree increases — your browser needs to download and parse a module before it can start downloading any modules it imports, which causes a waterfall effect.


Yeah. Can I put a file that imports all the modules at the top of the tree and load them before the browser has to parse anything else, to pre-cache? I haven't tried this yet.


In theory, but "no bundle step" becomes a much less attractive pitch once you have to start manually maintaining something like that.

IMO the advantage of ES module support in browsers is not needing to bundle in development (although with native bundlers like esbuild this is less of an issue) and for deploying small apps where the tree is shallow. If and when your codebase starts growing, you should use a bundler.


Thanks, this is helpful advice. Not having to bundle in development is what I really want. In production I could see it as a necessary evil for the moment.


ES6 modules and your entire source tree?


Yeah. You're right, that is a bit of a problem. On first load, this is 272 files. I'm checking it now for the first time and most of them are loading sequentially rather than in parallel, which is taking 3.7 seconds.


Going without linting, testing (automated testing anyway), type checking would make me a lot sadder.


A build step is not required for any of that. I use prettier and jenkins on a large js codebase that never gets transpiled, packed, or minified.


That’s true, I guess you can just run those locally. But if there’s CI in the picture the “build step” is a fait accompli, as far as setup and maintenance and so forth.


Github runs jenkins. Jenkinsfile specifies tests and linting. How would webpack make this easier?


Oh I didn’t say anything about webpack. I haven’t used the GitHub features you’re talking about but that sounds like a build to me!


Usually when people say "build" they mean something that creates a build product which is different from the original source files. This could mean compiling and linking c to object files / executables, transpiling TypeScript to javascript, bundling lots of javascript files to one big javascript file, going from readable js to minified js, etc. I've never heard of testing or linting referred to as building.

BTW, I'm not the one that keeps downvoting you to death!


Yup it's ugly and tedious. Something like imba is far superior with vastly better ergonomics.


What exactly does this offer over, say, SolidJS, which also has reactivity without a virtual DOM, and has a much larger community, ecosystem, etc? As do several other frontend frameworks. Does the world really need another one, unless it can offer something substantially different?


I’m always happy to see React alternatives but I’m not convinced HTML and CSS inside JavaScript was a good idea.


I tried reactivity once to help me understand the implementation details under the hood. I wrote https://fancy-pants.js.org/ for this where I attached mutation to scheduling a render cycle. But it doesn’t address rendering leaving that to the student.

Something that really fascinates me is the difference between diffing versus declaration of dynamic content. In React and Vue it is a runtime function to know what changed and how to apply it. In Ember and Svelte it is a compile time calculation based on markers in the template.

I guess it comes down to personal preference. I’m quite the fan of a declarative style template that gets converted/compiled to a machine under the hood instead of diffing at runtime.


Yeah the second I saw the string block I noped out. JSX works really nicely and I dislike weird template string stuff now. Same with vue.


No JSX for me either. I want plain HTML, CSS and JavaScript. All scoped within their files and domains.


I’ve found this to be needless indirection.

If your components are large enough to warrant this simply break them down.


Just gotta write your CSS carefully. It's not easy, it takes time to learn, but so does the constant juggling that is the React world. At least the first one is faster and won't be out of fashion in 5 years, since it hasn't been in a decade.

I also find separation of concerns conceptually more sound and powerful.


React was created in 2013, so it has in fact been "in fashion" for a decade now.


Yeah, hopefully that trend is dying and we can get some sanity back.


Not at all, React is still a juggernaut and the industry standard. It is really, really good at what it does and other libraries are often riffs on its design philosophy. Even if React itself goes away for some reason, those patterns are here to stay since it has left a permanent mark on the field.

These complaints sound a bit like saying "man, compilers are so complicated, hopefully this trend is dying and we can get some sanity back."


It's an interesting comparison. An optimizing compiler is an incredibly complex beast that makes developer's life almost incalculably better. Most of our stack would be unfeasible to write and maintain in assembly. I'm not at all sold that React remotely qualifies, in fact, I've yet to see any benefits to developers or end users.

>it has left a permanent mark on the field

It sure has, an indelible stain


> Most of our stack would be unfeasible to write and maintain in assembly.

The same can be said about libraries like React and websites. Honestly, your position is completely untenable because it's the classic "everyone is dumb except me." Ah yes, all these million and billion dollar companies decided to use React because they're bored and not because it brings any value. You're the only genius, if only everyone realized that the Ideal Website was just handcrafted js.

There are thousands of websites written in React today, and they bring value to their users. That's not disputable. To suggest that there's no benefits is just an astounding level of arrogance.


I’m far from being the only one and it doesn’t take a genius to see that our industry is driven by far more than objective value returned to developer or end user.

Just do a quick search on HN, Twitter, Mastodon. You’ll see that’s actually not at all hard to find articles questioning its effectiveness.

The epiphany I had a few years ago is that React is very good for hiring, being hired, managing a team and has very little to do with writing and maintaining a website. At least that’s what I tell myself to find some peace. If you don’t think the JS world is mad, I don’t know if I can convince you in a HN thread.


It’s actually fine and great and decently performant for making web apps. Declarative code is much nicer than mutating the DOM, components are a great way to reuse code compositionally, hooks are nice building blocks for reusable logic within them. The JS world is indeed mad but that doesn’t mean React isn’t a highly pragmatic way of building GUI applications.

Just because style, logic, and result are logically grouped it doesn’t mean you’re losing “separation of concerns”. If in fact your definition of that is so shallow it amounts to simply having things in different files, you can do that anyway. It’s an organisational choice.


The alternative is a custom DSL for templating which I find much worse. Even CSS has a DSL for conditional and programmatic logic which I only use because of the immense performance benefits it brings.


Certainly not in the form of strings, no. That's a nightmare of run-time debugging just to make sure the tags are all closed and you have the correct attributes.

Perhaps pair it with a typescript library for composing it. There's nothing sacred about HTML for describing the DOM, but that it what it was created for, and the language the browser will report it.


That ship has sailed long ago!


It has not. I’m right here at land, don’t intend to go and there are dozens of us here :)


This vDOM concept never really clicked for me. I always saw it as some sort of shiny feature that a web framework had to have in order to be considered for use during the last decade.

I've rolled a DIY web framework using websockets and a brutish technique where I simply set document.body.innerHTML to whatever the server pushes. Still haven't found a situation where this falls apart, but I am sure HN can invent a hypothetical that would make me ashamed of myself.


That won’t preserve things like event handlers and it will perform increasingly poorly as you need to render more often, but for simple use cases it’s probably fine!


The usual cases are scroll positions, selected text, form fields, event handlers, basically any state that isn't naturally captured in the HTML text. That's where the VDOM differ comes in handy, by modifying existing elements and keeping the structure similar.


VDOM is just an implementation detail that you shouldn't care about at all.

But React team went big on advertising it as some kind of virtue, and a lot of gullible engineers bought that idea.


This place if overcrowded with solution that provide the same benefits than yours, you should clearly put a section describing the benefits of "Why us instead of X" - because it's not clear what this project brings new to the table.


On the contrary, open source projects are not products, and this one (like many) is free to use, evaluate, and permissively licensed. So I'd encourage anyone who works on a JS framework to do as you please, don't worry about JS-fatigued HN commenters, and share your work with the world if you think others may like it. :)


While virtual DOM solution has problems, I believe Dioxus https://dioxuslabs.com/ can be a better solution than Solid/Svelte solutions. We should try to optimize it with macros and other memory techniques to make it fast, rather than giving it up.

People may hold opinion that Solid/Svelte are excellent. However that's going towards another direction. Virtual DOM decouples how you declare UI and how it's rendered/updated by framework. Solid/Svelte couple them.


> Reactivity Without Virtual DOM

I am confused. Why are the concepts of reactivity and of virtual DOM put together like this? Was Virtual DOM ever solving the reactivity problem?


This is in the same vein as Imba, which their memoized DOM.

https://imba.io


It looks like this guy has created multiple accounts and posted the same URL multiple times.


I wish more libraries would user tagged template literals instead of strings with custom expressions.

Then template expressions are just JavaScript and the library doesn't have to implement it's own limited and slower subset, like this seems to do.


I only read the title, but how is this different than something like Svelte?




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: