loading...

Faster App Starts With Prepack & Webpack

kayis profile image K ・2 min read

TL;DR Here is a GitHub repository with the example :)

As if we hadn't had enough tools already, things are getting worse... or better?

This time with a tool called Prepack!

A tool for making JavaScript code run faster.

"But Kay, isn't JavaScript blazing fast already?"

Maybe, who am I to tell you? I can barely set up Webpack by copy and pasting a config from GitHub.

Anyway, this tool is highly experimental and you should use it at your own risk, it might overheat your Firefox.

"So what exactly does it make faster?"

The startup of your application.

"And how does it achieve this?"

I have no idea!

But it seems to run your application code once and then saves the state after that run. Later it replaces your code with a simplified version that produces the same state.

This should allow you to write nice code, that may be slow, then run it at build time and generate not so nice code that is fast, when it gets started in the browser. This is achieved by means of Voodoo.

It seems that this whole startup thing is a problem of code produced by Webpack, so it's worth a try on your Webpack based projects. So that you have an idea if it's worth waiting for Prepack to stabelize so you can use it in production in the future(!).

There is already a Webpack plugin for this, so it's easy to integrate.

"Is there a catch?"

Yes, since you code is run (and rewritten) at build time, you can't simply write code that requires global browser APIs:

 import { h, render } from "preact";

 const app = (
   <div id="foo">
     <span>Hello, world!</span>
     <button onClick={e => alert("hi!")}>Click Me</button>
   </div>
 );

 const domRoot = document.getElementById("app");
 render(app, domRoot);

Because Prepack doesn't know much about document and such, but luckily JavaScript doesn't care much about you writing code that uses undefined references till you actually run the code, so I rewrote my simple example by exposing a global function that isn't run when when the script is loaded, like this:

 import { h, render } from "preact";

 const app = (
   <div id="foo">
     <span>Hello, world!</span>
   <button onClick={e => alert("hi!")}>Click Me</button>
 </div>
);

global.bootApp = () => {
  const domRoot = document.getElementById("app");
  render(app, domRoot);
};

Later, in my index.html, which is outside of my build process, I simply called window.bootApp(); to run my prepacked code.

In my example the size blew up from 24.7 KB to 50.6 KB, but the whole thing isn't about size, but about costly computations at startup time, so your mileage may vary.

Whelp, play around with it and tell me if it improved your life.

Posted on by:

kayis profile

K

@kayis

Taking care of developer relations at Moesif and creating educational content at fllstck.dev

Discussion

pic
Editor guide
 

"But Kay, isn't JavaScript blazing fast already?"

Javascript is pretty fast! And it's fast because modern browsers do this thing called just-in-time compilation, where it converts the unoptimized Javascript that you've written into highly-optimized machine code. But, the JIT itself is a relatively expensive process, so instead of compiling all your javascript, it runs your unoptimized code for a while, does a bit of analysis to determine what code would benefit the most from optimization, and then optimizes that code on-the-fly.

So what about all the code that the browser determines isn't worth just-in-time compiling? It sits there, unoptimized! The browser deemed that it isn't worth the runtime cost to optimize it. This is where prepack comes in.

Prepack does a bunch of analysis on your code to figure out what can be statically determined ahead-of-time. It can be thought of as doing a sort-of ahead-of-time compilation step, such that code that wouldn't otherwise be optimized by the JIT, is instead optimized ahead-of-time. And the benefit of an ahead-of-time compilation step is that it incurs zero runtime cost.

 

How does this compare to Angular Ahead-of-time compilation?

 

I don't know much about Angulars AoT compilation.

The few things I found right now were template pre-compilation and tree-shaking, so I don't know if it can do more. So it seems it replaces template strings with functions and removes dead-code.

Prepack runs you whole bundle once (in node.js) and replaces function calls and calculations with assignments, but it wouldn't remove functions you didn't call (no tree shaking).

I could imagine that if you got static templates, that you create at start-up time and if you have much setup code in angular, this could still improve everything a bit.

 

Thank you for your answer... it's very interesting