I believe build-time compilers will play a much more important role in JavaScript development in the coming years. I this post I will show how I reduced the payload size of my blog commenting feature from over 100k to 4.7k by switching to the compiler based framework Svelte.
To start. Here is a screenshot of the component:
What is JavaScript compilation?
In my opinion there are a few variations on what it entails.
Angular
Angular has a really started to embrace build-time compilation. In the Angular world it's referred to as Ahead of Time compilation (AoT). AoT is a process of converting domain specific language in Angular templates to pure JavaScript that can be executed by a browser. By domain specific I mean things like ngIf and ngFor. Basically anything that is specific to Angular, but foreign to browsers.
The alternative is doing compilation at runtime before the application loads. This approach is referred to as Just in Time compilation (JiT).
JiT is a bad option for production applications and should be avoided. JiT delays application rendering since the application can't load until compilation has finished.
On top of that, users have to download the angular compiler as part of the application. Angular's compiler accounts for roughly half the size of the Angular framework.
I like to say: If you are doing AoT you are shipping an actual JavaScript application to the client. If you are doing JiT you are shipping an Angular application that has to be converted to a JavaScript application at runtime.
React
Now that Angular has AoT, can we say that it is far superior to React?
Well, no... It is not fair to bring up AoT as a differentiating factor when comparing Angular to React. The need for AoT is mainly driven by the fact that so much of your Angular code lives in html templates. In React you don't have this issue since JSX compiles down to pure JavaScript. In that sense you can say that React already has a form of AoT built-in.
Svelte
Svelte takes build-time compilation to the next level.
Both Angular and React will compile your application code, but you still have to download a framework “runtime” for your application to work. This means the user has to download one or more extra files that represent the framework itself (e.g. react.min.js)
This is where Svelte is different.
Instead of leaning on a runtime your JavaScript code is compiled into pure JavaScript that can run “as is” without downloading any additional framework specific files.
The idea is to compile your application to something that resembles a hand coded application. Basically what it would look like if you built everything from scratch without using a framework.
Of course the compiled code has to follow certain convention with specific functions etc.
I think of Svelte as writing the “framework” at compile time based on the specific needs of your application.
This is great since everything is custom tailored to the needs of your application. It's the best way to trim the payload since you will never have to download any unnecessary framework code from a “one size fits all” bundle.
Tree Shaking
Some might say that frameworks will ensure that no unnecessary framework code is included by doing Tree Shaking.
This is only partially true since Tree Shaking is generally limited to the statement level (e.g. class, function, etc). This means you can't partially Tree Shake an entity (e.g.class) if you only use 10% of it . A statement has to either be fully included or excluded.
Of course, this changes if you throw the Closure compiler into the mix. I have an article explaining this distinction a bit more here.
Angular will likely base its Tree Shaking on the Closure compiler in the near future.
Closure compiler integration can be tricky, but I am really excited about the Angular team's Closure compiler work. I expect great things from this integration.
I have an article here about some of the challenges when working with the Closure compiler.
Converting Blog Comments to Svelte
Now let's see Svelte in action!
My blog is a home grown application with a custom commenting feature. My initial implementation was done using Aurelia.
First of all, Aurelia is an excellent framework. I want to make it very clear that the following section is not a knock on Aurelia, but rather a comparison between compiler based and runtime heavy frameworks.
Aurelia falls into the camp of a runtime based framework. Like with any modern framework it supports optimization of the bundle to meet the needs of specific applications. That said there is no escaping a sizable Aurelia specific download.
I built the Aurelia version of the commenting feature last year. Here is an article detailing my approach.
Aurelia Payload
Granted this is not running the latest version of Aurelia (aurelia-framework@1.0.0-beta.1.2.3), and there might be ways to tweak the bundle a bit. In my case the Aurelia framework bundle alone weighed in at 84.7k. In addition there is 2.7k in application code and another 21.3k from systemjs.
Svelte Payload
I recreated the commenting feature in Svelte and my payload is sitting pretty at 4.7k. This is the entire application!
Closure Compiler
The 4.7k payload is after running the bundle through the Closure compiler (ADVANCED_OPTIMIZATION). However, a non minified raw version of the application is only 8.9k.
All numbers are after gzip.
You can compare the source for both here:
The improvements from the Closure compiler are in part from standard minification, but Closure compiler will also run deeper analysis to look for improvements.
One example of this is the createElementX wrapper functions in the Svelte compiled code. This function exists in a few different versions. If you look at the Closure compiled source you will see that all the different versions of createElementX have been inlined away.
Closure compiler is probably not necessary here, but it makes for an interesting experiment.
It's usually difficult to get the the Closure compiler's ADVANCED_OPTIMIZATION to work with most frameworks. It typically makes too many assumptions about how the code is written to be practical.
Svelte however is actually relatively compatible with the Closure compiler. This is mainly because Svelte will compile the entire application to JavaScript.
Frameworks with a separation between JavaScript and html will typically not fare well with the Closure compiler. This is because Closure compiler will often shorten property names in the JavaScript and break bindings in the html.
The second hurdle is often a runtime that is not compatible with the Closure compiler. To get around this you have to give the Closure compiler cues in the form of “Externs”, which typically complicates the integration.
Svelte doesn't have html separation or a runtime, so things are easier. There are still a few adjustments here and there, but the ones I've seen are often not Svelte's fault.
So far I've only seen one api in Svelte that may not be great for the Closure compiler.
The “component.get('firstName')” api with string based accessors will break if property names are shortened by the Closure compiler. You can work around it though in your objects by specifying property names in quotes like this:
This becomes harder to do if you are using the property in a component binding like this though:
Demo
I have converted my blog to use the new Svelte component. Check it out here on one of my articles with several comments.
I decided to keep the Aurelia version for my Aurelia specific articles. For comparison you can see the old implementation here.
I have also pushed my Svelte code to Github.