DEV Community

Discussion on: Why Efficient Hydration in JavaScript Frameworks is so Challenging

Collapse
 
mhevery profile image
Miško Hevery

I had a most excellent chat with Ryan offline and wanted to share my thoughts here, which boil down to "Qwik is truely resumable." Maybe not right this moment, as we have a few more things to implement, but we have designs for all of these.

To be truly resumable, you need to have a few things:

  1. As Ryan pointed out, you need to attach to existing components out of order.
  2. You need to serialize the whole state of the application.
  3. You need to understand which component knows about what state. (Subscriptions)

If you don't have #3, then once the data changes, you don't have a choice but to re-render the whole app because you don't know which components are invalidated and need to be re-rendered. This forces you to download the whole app, and that is expensive.

But Qwik does know the answer to #3. Though Proxys Qwik knows which component cares about which data, Qwik serializes all of this information to the DOM. Then the data gets mutated Qwik can run querySelectorAll on HTML, which will tell it which components are subscribed to the data and hence have to be re-rendered. This is significant because it allows most of the components to stay in their unhydrated state for the duration of the app's life.

I think of this problem as layers in onion:

  1. Most obvious is how to serialize listeners. (If you can't do that, than you have to rehydrate)
  2. How do I render components out of order (If you can't do that that you will be forced to render parent/children)
  3. Know which component is invalidated on state change (If you can't do that, you will be forced to re-render the whole app)
  4. Have ways to watch data without running the code (useEffect equivalent) (If you don't have that you will have to eagerly download code)
  5. Have ways to deal with data that is not Serializable. (if you can't do that, than the kinds of apps you can build will be limited)

On each of the levels, there are interesting problems to solve. Qwik's aim is to solve all of them, and so far, we have been able to do that and have plans for the reminder, which is consistent with Qwik's mental model.

This is why I think that Qwik is (will be) truly resumable.

Thread Thread
 
btakita profile image
Brian Takita • Edited

I appreciate both yours & Ryan's work so this thread is gold. Thank you for listing out the hydration scenarios. Here is my concern as an app & library developer. I really like isomorphic JS & am building around that concept. Up till now isojs hydration has been a black box. Making the isojs hydration process more transparent & even programmable will be appreciated.

I would like to learn more about your plans re: step 4 & 5. Also, do you forsee Qwik or its concepts as being compatible with Solid & other isojs component libraries?

Thread Thread
 
ryansolid profile image
Ryan Carniato

I love Misko's framing here as 4/5 are big part of what we are working on with Marko as well. 4 is a fairly natural extension of 3 in the same way Solid's granular reactivity is over say MobX + VDOM. The way to do this involves serializing the dependencies, then you don't need to run it once. In Marko's case it is a compiler that detects these similar to Svelte, except Marko's works cross file and traces dependencies even hoisted. For Qwik it's runtime similar to Solid, just that if it runs on the server you know the dependencies from the last run and can continue only when one updates. The way they serialize them is simply to take the key off their shared proxy store that all components use (that manages the Dependency injection).

I do want to talk to Misko more about this because there are different types of scenarios. Derivations definitely want to run on the server and be treated this way but effects in most frameworks are also a way to do client only code that you don't run on the server, like include a jQuery chart. In those cases you'd want the effect to run at hydration time so it isn't really a problem since you don't need it to run on the server to collect dependencies. In Marko we make that deliberation that it is effects and event handlers that run at hydration time. The interesting piece for Qwik is do they code split on this too putting the effects in with the event handlers and separate from the other component code.. once you follow this thinking Qwik may actually approach closer to how Marko 6 is breaking up code.

For Marko we actually split it from input/props through Reactive graph so a component consists of many different fine-grained pieces. Which is interesting since instead of things being component level the parent can know based on whether it passes static or reactive data to even import the dynamic child part to run it. And since we know exactly what can be stateful we can eliminate whole subtrees through the component structure.

I'm excited to talk more about this because the implications get pretty interesting. Because even when things are stateful instead of serializing everything we can optimize it further to serialize things based on the leaf nodes of the reactive graph. It's only things read from an DOM, event handler or effect directly that need to be serialized. With one except reactive convergences need each source branch serialized as well so one side can update without needing to run again. In a sense I've shown how Marko today can only serialize data that is used in components based on what is passed in to top level components input/props. With this approach we'd get field level serialization of only what is actually used/usable. Because of convergence we know that and in other cases we know that it would be impossible change what data we are seeing without refetching (ie triggering an async source) so we can be confident we don't need to have it in the browser initially.

Of course this all comes down to what is serializable. I'm interested to learn more about Qwiks approach. With Marko since the compiler sets the boundaries we can always step up one level with serialization if it isn't serializable as long as all root sources are. But we are working on techniques to serialize typically unserializable things like promises or functions and closures.