Dynamic imports: Speeding up the initial loading time of WebAssembly Studio

First of all, for you that haven’t heard,

“WebAssembly.Studio is an online IDE (integrated development environment) that helps you learn and teach others about WebAssembly. It’s also a Swiss Army knife that comes in handy whenever working with WebAssembly”. — Michael Bebenita
WebAssembly.Studio

Loading large dependencies

WebAssembly Studio utilizes an open source code editor called Monaco Editor that is also used to power the oh-so popular VSCode. The size of the Monaco Editor dependency is pretty extensive, roughly around 15mb. This becomes smaller when minimized and gzipped of course, but it is still a lot to download on an initial page load (especially for users with slow connections).

The scenario below is a bit simplified but explains (in broad terms) what used to happen on the initial page load. Before the user interface (UI) could be rendered, the whole Monaco Editor dependency had to be loaded.

1. User visits http://webassembly.studio
2. Load the whole Monaco Editor dependency
3. Render the UI
4. Register language support

Dynamic imports

await import("monaco-editor");

The new import syntax (currently a ECMA stage 3 proposal) introduces a new dynamic way to import modules. As apposed to the static import "module-name" that can only be used at the top level of a file, import() returns a promise and lets you import modules on-demand or conditionally. Lets look at the differences.

// The old way
import moduleA from "moduleA";
import moduleB from "moduleB";
function doStuff(condition) {
if (condition) {
moduleA.doStuff();
} else {
moduleB.doStuff();
}
}

In the example above, both moduleA and moduleB will always imported regardless of if they are actually used.

// The new way
async function doStuff(condition) {
if (condition) {
const module = await import("moduleA");
module.doStuff();
} else {
const module = await import("moduleB");
module.doStuff();
}
}

By using dynamic imports we can import the module on-demand, conditionally when we actually need it.

Speeding up the initial page load

After doing some digging it turned out that only about half of the Monaco Editor dependency was actually needed the render the UI. The rest could be imported later on when needed. This would in theory make WebAssembly Studio load twice as fast.

To accomplish this, we first identified which parts of the Monaco Editor that was needed to create a new editor. By adding some optimization options to our webpack.config.js we managed to split the Monaco Editor dependency in to two different bundles . One called monaco-editor.bundle.js and one called monaco-languages.bundle.js — each about 7.5mb.

When the user now visits http://webassembly.studio, the following stuff happens (again this is simplified a bit).

The user does no longer have to wait for the full Monaco Editor dependency to load before interacting with the UI. Now only the bundle that is containing the stuff needed to create a new editor have to be imported before the <App/> component can be rendered. After the UI is rendered, we import the language bundle and register language support. Instead of having the download the full 15mb we now only need to download half.

How about your dependencies?

Think about your dependencies. How big are they? Which ones have to be loaded before you can render your UI? Chances are that you might find dependencies that is not needed initially and therefore can be dynamically imported when the user invokes certain functionality instead. The same goes for big dependencies such as the Monaco Editor. Is it really necessary to load the whole dependency before rendering the UI, or would it be possible to split it up into different bundles that can be loaded on demand?

Tools for investigating

I recommend using the webpack-bundle-analyser to analyze your dependencies. It provides a interactive zoomable treemap showing the generated bundles (and whats included in each of them). To find out when each bundle is loaded, fire up the chrome dev tools and look at the network tab.

Need any help getting started with dynamic imports? Hit me up over at slack http://wasm-studio.slack.com (For an invite go here: https://wasm-studio-invite.herokuapp.com/).