Remote-DOM aka Virtual Virtual DOM

Written by AviMarcus | Published 2017/03/07
Tech Story Tags: javascript | react | webworker

TLDRvia the TL;DR App

Trust in the browser is an all or nothing affair, you either run a snippet of JavaScript in the main document on the main UI thread, with access to every global, to any part of the DOM, or you run it in an isolated environment either an iframe where it is constrained to a small rectangular box or a WebWorker where it can’t directly affect the UI at all.

I wanted more, using multiple iframes makes sites slow, it forces the browser to parse/compile/evaluate the same JavaScript sources multiple times, and makes your UI contained to these small boxes…

I wanted to be able to write React components, run them without having to trust their code, and have them draw to the same DOM. I found a nice open source library react-worker-dom, and started tinkering with it.

React-Worker-DOM is a heavily modified fork of React which consists of two complimentary halves. One where your actual React code resides and where every line that was supposed to touch the DOM was changed so it would eventually postMessage from a WebWorker back to the main frame. The other half, interprets these messages, applies them to the DOM & listens to events sending them back in the opposite direction.

The projects’s focus was on the performance benefit of running most of your code inside a different thread via a WebWorker, and it worked beautifully. Although it didn’t support a few features I needed like a pluggable transport layer and multi-tenancy, these were relatively easy to add, and I even went through with the effort of porting the code to newer React 0.14 to get a sense of how hard it was to keep up with newer React versions.

I could almost convince myself the solution was good enough, but when a friend actually asked me whether it was good enough to use in production, I hesitated, it was complicated, fixing bugs in it was difficult, porting it to newer versions of React a nightmare. I knew using it meant condemning myself to a continual effort of maintaining something that was just not maintainable…

I found out ages ago that when things are hard it usually means I’m doing it wrong. There had to be a saner way to get the benefits we needed without this ongoing tax on my time & happiness. A lot of software engineering is about learning where to draw the boundary lines between different pieces of the puzzle, what is unique about the task at hand, what should be reused and what should be cast aside.

I went for a long walk, trying to find alternative ways to solve the problem, and then it hit me. I needed something that is a lot more stable to polyfill or patch, and React was a too fast moving target, I had to remove React from the equation. Which only left the browser DOM API itself, React almost exclusively writes to the DOM without doing any reads, all I had to do was supply a facade that provides the tiny set of browser APIs React expects and I could get all the benefits without modifying a single line of React’s code, thus Remote-DOM was born.

Remote-DOM is a tiny library with less than 1k lines of code, with two halves remote and local. The remote half, supplies a browser facade, a virtual window and document, to other libraries letting them use the APIs they know and expect the browser to supply like: document.createElement window.addEventListener and so on, without having any actual DOM, and sends these commands back as JSONs to the local half. The local half of the library interprets that JSON and does the real creating of DOM nodes and adding event listeners, and when an event happens it send it’s information back to the remote side as JSON, all you need to do to connect them is give them a way to communicate between them.

A single significant limitation of this approach is that because the communication bus is asynchronous, there are a few things you just can’t do transparently, you can’t read a DOM node’s offsetHeight and expect the value to be there and you can’t respond to events within the event loop, to mitigate this I added a new concept of native invocations, a small dictionary of verbs defined in the local DOM side of the library that you can invoke from the remote side. This lets you for example play a video on click in mobile safari where the browser won’t play a video unless it is was triggered within an event loop.

So what can you do with it? Gain performance from removing React from the main UI thread, build a truly distributed ad network that doesn’t need to run any code in the browser except for the remote-dom libary and thus can connect fluidly to dozens of ad providers, or maybe just maybe create the next evolutionary step of React - using server side rendering with React is awesome but in the time between HTML loaded and all your client code fully ready you have a dead static website that doesn’t respond to user events, what if we changed it so the server rendered using remote-dom and kept handling user interactions until the client was ready, at which time the client would get a snapshot of the redux store and switch over to handling the events itself… But that is a story for a different post ;)

Anyway it’s released, it’s open source (thanks Wix), have fun.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.

To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.

If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!


Published by HackerNoon on 2017/03/07