The Marko Team has been working on a new rendering engine which is slated to become the core engine for Marko in a similar way Fiber(React), Glimmer(Ember), and Ivy(Angular) have been for their respective libraries. Today I want to give you a first peek into what this is going to look like.
A lot has changed since the release of Marko 4 in 2017. Most of the effort has been managing migrations, and updating tooling (ie.. the move to Babel, Webpack, Rollup). Marko 5 is in alpha and represents the modernization of the toolchain. But what about the architectural considerations?
The FLUURT(Fast Lean Unified Update and Render Target) engine is being developed with a few key goals in mind:
- Improve client-side performance
- Improve development experience
These are an acknowledgement of the increasing need for a dynamic and interactive experience on the client. Marko has long had one of the best server-side implementations but as frameworks like Next show up, and even newer compiled approaches like Svelte it is clear it is time to take the next steps.
Updated April 2022: FLUURT as a codename has been has been superseded by Marko 6 now that it is clear that these changes will be arriving in the next major version. I will leave the original article intact but you can treat these as equivalent.
Marko is an interactive templating language first and foremost so we should play to our strengths. We have the ability to compile our templates as desired. So to best accomplish our goals we've decided to attack the problem by building a new foundation on the client.
Being a declarative language with control over templating syntax, using the semantics of fine-grained reactivity is a clear way we can achieve both our goals. Relying on a small set of reactive primitives with code generation drastically reduces runtime size and complexity.
The approach the FLUURT is using is what I'm calling fine-grained compile-time reactivity. This is basically a hybrid between what Svelte does with its compiler and fine-grained reactivity found in libraries like Vue, Solid, or MobX.
The way this works is pretty novel. In a way it is very similar to how Svelte compiles away the reactive system. But instead of compiling things into components that re-run on state change(thanks to a
$invalidate call), FLUURT splits a component into multiple functions. One for each reactive atom(signal) that when executed with a new value conditionally calls any downstream work.
And this extends beyond a simple template as these functions are exported so that parent consumers of the component can selectively import the methods they need if the data they pass in is dynamic. Of course, this is all automatically handled by the compiler so the developer does not need to do anything special.
The end result is compiling away the reactivity but with an execution model very similar to something like SolidJS. Marko basically compiles away any notion of components.
Language design can be challenging but we know that it is of utmost importance to make things consistent. To accomplish this we want to bring reactivity into the language of Marko in an extensible way.
The proposal is that our primitives are just Marko tags. This means that they can be co-located, nested, and composable. Co-located means that they can live in the template where they are used; nested means that they can be mounted/unmounted independent of the component; composable in that they can be constructed and extracted independently of the component file.
One would define a reactive value (ref/observable/signal) with a
let tag. And a derivation (computed/memo/$) with a
const tag. And writing your own can be used and consumed in the same way.
The ability to put these primitives nested in the template creates a cut and paste development experience, where the cost of refactoring is greatly reduced as code can mostly be moved around at will without change.
From these parts you might be able to see that most of the library works independently of components. One benefit is this approach reduces the overall overhead of having components.
But more interesting, is that this allows for a new type of hydration. We can hydrate along reactive boundaries rather than component ones. We can split the stateful and static parts of the template and ship only parts of components and their descendants to the browser.
Classically with partially hydrated apps, as you might find in Marko or ElderJS, once you hit a stateful component you need to have all the JS code below that point. But FLUURT introduces the ability to break up our islands even smaller. It's more like Hawaii than Taiwan.
The amount of end-user code shipped to the client can be drastically reduced.
There is a lot to be excited about in the upcoming FLUURT engine. It unlocks performance techniques yet to be seen in any major framework. It provides a development experience where writing less code isn't just about the number of characters you commit. And it finally gives Marko the tools it needs to be as much of a force in the client as it has been on the server.
This is just the introduction. Look forward to follow-up articles where I will dig into each area in more depth.