However, attaching event handlers to the Document Object Model (DOM) is not the most difficult and expensive part of hydration.
This article explains why I believe hydration is an overhead. This is a memory consuming and slow booting hack especially on mobile. For the purposes of this article, let’s define overhead as work that can be avoided and still lead to the same end result.
Digging deeper into hydration
Miško Hevery is Chief Technology Officer at Builder.io. As CTO, Miško oversees the technical department that powers the Builder.io application and software. Before he joined Builder.io, he created open source platforms for Google, including Angular and AngularJS, and was a co-creator of Karma. During his time at Google, he brought the testing culture to his Google with his blog. Before focusing on making the web better, he believes testing is the key to success.
The hard part of hydration is knowing which event handlers you need and where they should be attached.
- what: An event handler is a closure that contains the behavior of an event. What should happen if the user triggers this event.
- Where: location of DOM element where WHAT should be attached (including event type)
An additional complication is what is the closure that is closed APP_STATE When FW_state:
- APP_STATE: application state. APP_STATE What most people think of as states.without it APP_STATEyour application has nothing dynamic to show to the user.
- FW_state: The internal state of the framework.without it FW_state, the framework doesn’t know which DOM nodes to update or when the framework should update them. Examples include references to component trees and render functions.
So how, what and where to recover? Download and run the rendered component in HTML. This is the expensive part.
In other words, hydration APP_STATE When FW_state By eagerly running the app code in the browser, it includes:
- Downloading component code.
- Running component code.
- what to recover (APP_STATE When FW_state) and WHERE to get the event handler closure.
- Attach WHAT (event handler closure) to WHERE (DOM element).
Let’s call the first three steps the recovery phase.
recovery That’s when the framework is trying to rebuild your application. Rebuilds are expensive because the application code must be downloaded and executed.
Recovery is directly proportional to the complexity of the page being hydrated and can easily take 10 seconds on mobile devices. Recovery is the expensive part, so most applications have sub-optimal startup performance, especially on mobile.
Recovery is also an overhead. Reconstructs information already collected by the server as part of server-side rendering (SSR) or static site generation (SSG). Instead of sending the information to the client, the information was discarded. As a result, the client has to do a costly Recovery to rebuild what the server already had. Recovery could have been avoided if only the server had serialized the information and sent it along with the HTML to the client. The serialized information saves the client from having to eagerly download and execute every component in her HTML.
In other words, the re-execution of code on the client that the server has already executed as part of SSR/SSG is what makes hydration pure overhead.
Resumability: a no-overhead alternative to hydration
To remove the overhead, the framework should not only avoid Recovery , but also attach WHAT , step 4 above, to WHERE .
Avoiding this cost requires three things:
- All necessary information serialized as part of the HTML. WHAT, WHERE, APP_STATEWhen FW_ state.
- A global event handler that relies on event bubbling to intercept all events. This eliminates the need to actively register every event individually on a particular DOM element.
- A factory function that can lazily recover event handlers (WHAT).
The above setup is resumable because it allows the server to pick up where it left off without having to redo work it has already done. By lazily creating her WHAT in response to the user’s event, we avoid all the unnecessary work that happens with hydration. All this means no overhead.
A DOM element retains event handlers for the lifetime of the element. Hydration creates all listeners eagerly, so it needs to allocate memory on startup.
Resumable frameworks, on the other hand, do not create event handlers until the event is triggered. Therefore, it consumes less memory than hydration. Additionally, the event handler is freed after execution and the memory is returned.
In a way, releasing memory is the opposite of hydration. It’s as if the framework lazily hydrates certain WHATs, executes them, and then dehydrates them. There is not much difference between his 1st and nth execution of the handler.
To bring this idea to life, we built Qwik, a framework designed around “resumability” and enabling rapid startup. To demonstrate the impact of resumability, we created a demo of a ToDo app running on Cloudflare Edge. The page becomes operational in about 50 ms.
Simply put, hydration is overhead due to duplication of work. The server uses WHERE and WHAT (APP_STATE When FW_state), but the information is discarded instead of being serialized for the client. The client then receives HTML that does not have enough information to reconstruct the application. Due to the lack of information, the client must eagerly download and run the application to restore the WHERE and WHAT.
An alternative approach is resumability. Resumability focuses on transferring all information (WHERE and WHAT) from the server to the client. Only user interactions force the client to download the code that handles that particular interaction. The client is not duplicating work from the server. So no overhead.