Andrew Welch · Insights · #JavaScript #frontend #SystemJS

Published , updated · 5 min read ·


For more tools, technologies, and techniques, check out the devMode.fm podcast!

Using SystemJS as a JavaScript Loader

The time is now for fron­tend web devel­op­ers to stop using <script> tags, and start using a JavaScript loader like Sys­temJS. Here’s why, and how

Loader At Work

If you’re a fron­tend web devel­op­er that’s accus­tomed to using <script> tags to load in your JavaScript code, you’re going to want to break that habit. Don’t wor­ry, I’ll explain, and see if I can’t con­vince you.

Gone are the days when you might include jQuery and a cou­ple of oth­er JavaScripts on the fron­tend. Now entire sites are being built via JavaScript. Even if you’re not embrac­ing the whole JAM­stack thing — and like any­thing else, there are down­sides and rea­sons not to: pick the right tool for the job — build­ing a mod­ern web­site is get­ting more and more complicated.

Web­sites have mor­phed from design-dri­ven Dig­i­tal Brochures” to devel­op­er-dri­ven full-on cus­tomized appli­ca­tions, as dis­cussed in the A Pret­ty Web­site Isn’t Enough article. 

The reason you should be eschewing <script> tags and using a JavaScript loader like SystemJS is to help you manage this complexity in a structured way.

I’m going to approach this arti­cle from the point of view of a fron­tend devel­op­er who uses a vari­ety of third par­ty and cus­tom JavaScripts to pro­gres­sive­ly enhance and as a com­pli­ment to the HTML & CSS on the website

Link A Token Example

So for exam­ple, let’s say we’re build­ing our web­page, and we need to include jQuery and a half-dozen oth­er JavaScripts, some that depend on jQuery and some that don’t.

Nyc Token

Then we have our own cus­tom JavaScript that we sprin­kle around. It’s too small to jus­ti­fy putting it into a sep­a­rate .js file, so we inline it to do things like click han­dlers, and so on.

Okay, no prob­lem, we add all of the <script> tags to the <head> and away we go! Ooops, but wait a minute. Putting <script> tags in the <head> is bad, because it’ll cause ren­der block­ing

Alright then, let’s put the <script> tags at the bot­tom of the web­page just before the </body> tag. Ooops, hold on, we have some inline JavaScript on some of our pages that depend on those scripts being loaded.

Fine! We’ll put the <script> tags back in the <head> but we’ll make them async or deferred so they don’t ren­der block. Now our web­page ran­dom­ly throws JavaScript errors, because the async loaded JavaScript may or may not be loaded by the time our inline JavaScript is executed.

Alright, we’re gonna give up and just put some scripts in our <head> that must be loaded ear­ly, and put the rest down before our </body> tag, and take the per­for­mance hit of ren­der block­ing. But now one of our scripts that we want to use in our inline JavaScript isn’t work­ing, because it depends on jQuery.

Sigh. So we move jQuery up into <head>, too, along with this script. Every­thing final­ly works, but Google Page­Speed Insights hates us, it’s frankly a mess, and is pret­ty frag­ile if we start intro­duc­ing more com­plex­i­ty to the mix.

Calgon, take me away!

Link It’s all about Dependencies

This sce­nario is a small-scale exam­ple of how things can get out of hand even in a pret­ty sim­ple set­up. When we start adding more third par­ty scripts, and more cus­tom JavaScript of our own, it breaks down pret­ty quickly.

What we real­ly need is some­thing that will man­age our depen­den­cies for us — ensur­ing that jQuery is loaded before any JavaScripts that need it, for exam­ple — and will take care of load­ing our JavaScripts in a per­for­mant manner.

That’s pre­cise­ly what Sys­temJS can do for you.

You tell it to load a JavaScript, and it will load that JavaScript and all of the JavaScripts that it depends on to run. It also will load it asyn­chro­nous­ly, so we won’t be ren­der block­ing, and we can use it both on the fron­tend in our web brows­er, and on the back­end in our Node­JS code. Cool.

Now, this is the JavaScript land­scape we’re talk­ing about, so there many ways to skin the same cat. In the past, I’ve used Require­JS for this pur­pose, and it’s served me well. How­ev­er, Sys­temJS is track­ing the JavaScript ES6 spec, so we’re real­ly future-proof­ing here.

If all of this seems like a bit much— “I just wanna load some JavaScript” —believe me that getting it up and working is not that hard, and it will pay dividends going forward in spades.

Less time wor­ry­ing about depen­den­cies, debug­ging weird JavaScript errors, and more time build­ing your web­site on a sol­id, scal­able foun­da­tion. Plus, you can write your JavaScript code in the fan­cy ES2015 for­mat even if your browsers don’t sup­port it.

What is this sor­cery? I won’t get too deep into the nerd­stuff here. We’re not going to use jspm (indeed, I’ve been reduc­ing the pack­age man­agers I use) or wor­ry about any of SystemJS’s more eso­teric fea­tures, but rather just focus on the prac­ti­cal appli­ca­tion of Sys­temJS on a web­site. A good read is the ES2015: A Sys­temJS Odyssey arti­cle if you want more.

Tan­gent: Sys­temJS can actu­al­ly do a whole lot more, even doing super cool stuff like run­time dynam­ic tran­spiling of your JavaScript from things like Type­Script, ES6 JavaScript, and so on as they are loaded. It also sup­ports the load­ing of all of the major JavaScript mod­ule for­mats such as ECMAScript Mod­ule, Com­mon­JS, Asyn­chro­nous Mod­ule Def­i­n­i­tion, and old skool Glob­als shims… so you don’t have to know or care how they were writ­ten. Until there is full brows­er sup­port, you’ll still need to tran­spile any ES2015 JavaScript, either at run­time via a Sys­temJS Babel plu­g­in, or as part of your build process.

Link Let’s Get to the Code!

Okay, so if you’ve read this far, I assume you’re on board… or at least you’ve decid­ed you might as well fin­ish what you start­ed. So let’s have a look at how this will all work.

First of all, we’ll allow you one exter­nal script tag on your web page, some­thing that looks like this: 

<script src='/js/system.min.js'></script>

Looks pret­ty nor­mal, this just loads in the systemjs.min.js script. Then we need a way to tell Sys­temJS where our JavaScripts are locat­ed, and we can do that either in a sep­a­rate JavaScript we load in, or just via inline <script> tags:

<script>
System.config({
    baseURL: '/js/',
    paths: {
        'lazysizes': 'lazysizes.min.js',
        'prism': 'prism.min.js',
        'vue': 'vue.min.js',
        'vue-resource': 'vue-resource.min.js',
    },
});
</script>

These con­fig set­tings can go any­where; all that’s required is that Sys­temJS is loaded already, so that the System object is avail­able to allow us to set its config options.

There’s a ton more you can set up here, but this is all you need for a sim­ple set­up. You tell Sys­temJS what the baseURL is for where all of your JavaScripts are locat­ed, and then in the paths you just map the name you want to refer to the JavaScript by par­tial path name (used in con­junc­tion with baseURL) or to the full URL to the JavaScript itself.

Now let’s say we have a bit of code that depends on Vue­JS, we can just do this:

<script>
    SystemJS.import('vue').then(function(Vue) {
        console.log("Vue and it's dependencies are now loaded!");
    });
</script>

We’re telling Sys­temJS we want vue loaded, and it will go off and asyn­chro­nous­ly load it and all of its depen­den­cies, then exe­cute the call­back func­tion when it’s all ready to go. So we can put any code that depends on Vue being loaded in there, and away we go!

We no longer have to wor­ry about the order in which our <script> tags are list­ed. We just say what we need, and it gets loaded. Since Sys­temJS knows all about var­i­ous mod­ule for­mats, we can do things like:

import $ from 'jquery';
import lazySizes from 'lazysizes';
import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

// Do something

And then if we did:

<script>
    SystemJS.import('main').then(function(m) {
        console.log("main.js, jQuery, LazySizes, Vue, and Vue-Resource are all loaded!");
    });
</script>

When Sys­temJS goes to load this main.js JavaScript, it knows we want all of those mod­ules import­ed, and it’ll load them for us as well. Sweet.

This approach of keep­ing JavaScript mod­ules in sep­a­rate files that are loaded on-demand works bet­ter for http2 than glob­bing every­thing togeth­er into one mas­sive JavaScript file. And it makes it log­i­cal­ly eas­i­er to man­age as well.

The web­site you’re read­ing right now uses Sys­temJS to man­age all of its JavaScript depen­den­cies, and load them asyn­chro­nous­ly as needed.

This struc­tured depen­den­cy approach is much more scal­able than ran­dom sprin­kling your HTML pages with <script> tags. It works on the fron­tend in-brows­er, and on the back­end in Node­JS. And it’s where the future of JavaScript lies as well.

So get on board now…