Javascript Module Loaders

Javascript has a long history with so many changes over the years. One area that has so much improvements yet might still be so confusing is its support for modularization.

This blog post discusses a part of this area, specifically on how to your Javascript source files can be loaded into your application. This blog post also focuses on Javascript on browser instead of other native environment such as NodeJS.

First of all, let’s assume your web application has multiple files (or can be seen as modules) due to some benefits below:

  • A gigantic module/file is very hard to read and understand.
  • A gigantic module is hard to reuse.

In the next sections, I’ll go through some main ways to load your Javascript into a web page in a web browser.

Script tag

This is the first and most basic ways at the moment to load Javascript in browser (the other way is to call eval with a Javascript string):

If you split your applications into multiple Javascript file, you’ll need a <script> tag to load each file into the browser. This has several disadvantages (with workaround that will be discussed in next section):

  • By default, <script> tags load and executed in sequence from top to bottom. While a <script> is loading or executing, page rendering is also blocked.
  • When you have a lot of Javascript files in your application, overhead from sending that many web requests to get each script one-by-one will start to get substantial.
  • <script> tags have to appear in the correct order. If your Javascript file requires JQuery, you have to make sure your code is put after JQuery’s <script> tag or you will get “$ is not defined.” Figuring out the correct order gets more and more tricky when you start to have more and more files.
  • Modern browsers introduce async and defer attributes on <script> tag to allow asynchronous loading of the script (script loading in the background won’t block page rendering). However, all your async <script> tags will start loading immediately when their tags are rendered, and there is no way to make sure one script is executed before another to guarantee the dependency constraint.

Recently, modern browsers starts support for the new ES6 (ES2015) import syntax:

This is a great step toward having a standard module system directly in the browser. However, as with any other web standard, one solution cannot solve all problems. You will still have the same large number of web request if your application have lots of small modules. There are also some limitations in current browser support mentioned in the section “Other differences between modules and standard scripts” in the link above. Moreover, older browser such as IE will completely ignore <script> tag with type=”module”, so your page will stop working on those browsers if you rely on this feature.

Require.JS

Require.JS is a Javascript library that automatically and dynamically load the dependency Javascript files at runtime in the correct order. It works with 2 other common dependency indicator formats:

CommonJS format

Asynchronous Module Definition (AMD) format

Note: Require.JS website provides a very interesting analysis on why AMD format is better for browser environment https://requirejs.org/docs/whyamd.html. There is also a lot of discussion on module formats in Javascript, which is a complicated topic and won’t be in the scope of this blog post.

Require.JS doesn’t introduce or require any recent Javascript syntax and can run on very old browser such as IE6.

Module Bundler

Module bundler is a tool to analyze and bundle all your source files into a single Javascript file at build time while retaining the correct dependency order. This way, your source code can be spread into multiple files while your HTML needs just 1 <script> tag:

Using popular bundlers such as Browserify or Webpack (my personal favorite), you can define your module dependencies using CommonJS format as in previous section, or the new ES6 (ES2015) import syntax:

Webpack and Browserify also allow you to include extra steps into the bundling pipeline such as conversion (or transpilation) that convert syntax that might not normally understood by browsers to run in browser environment.

Let’s discuss some advantages and disadvantages of module bundlers.

Advantages

  • Less file to load: since all your codes are bundled into 1 or a few files, you can split your application into lots of small modules without worrying about the large number of web requests to load all your source files.
    Moreover, module bundlers do not always produce a single output file. Webpack supports optimization techniques such as chunking that produce multiple output files based on certain rules to optimize caching and first load time.
  • Dependencies are resolved and bundled at build time, so you don’t need a module loader such as Require.JS at runtime (hence save some more loading time on the browser).
  • Enable a really complicated build process. A very common build pipeline would be: run all unit tests, then transpile code to a certain Javascript version, then bundle all codes into a single file with correct dependency order and minify the file with source map, etc. Webpack can even process other web resources such as compiling/minifying your css and inlining small images.

Disadvantages

  • Once you get familiar with module bundlers, you might start to add more and more complicated steps and might feel overwhelm when you need to upgrade some package or fix some build-related bugs. This might not worth it for small projects.
  • Since you now require a build step to generate the actual file used by browser, your front-end development loop also get more complicated. You cannot simply change the file and refresh the browser to see the result without triggering the build process. However, module bundlers usually work with simple tools to watch changes on your file and automatically trigger the whole build process.

Summary

In brief, if your web application rely on very little Javascript, you can start with simple <script> tags to load each of your .js file. However, once your Javascript codes grow enough that you want to break them into modules, you should start looking at Require.JS (if you still need to support old browsers) or built-in support for “import” in modern browsers. When the number of Javascript files that are loaded concerned you, module bundlers would give you the capability and flexibility that you have never had before to build and optimize your applications.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.