Andrew Welch
Published , updated · 5 min read · RSS Feed
Please consider 🎗 sponsoring me 🎗 to keep writing articles like this.
Using SystemJS as a JavaScript Loader
The time is now for frontend web developers to stop using <script> tags, and start using a JavaScript loader like SystemJS. Here’s why, and how
If you’re a frontend web developer that’s accustomed to using <script> tags to load in your JavaScript code, you’re going to want to break that habit. Don’t worry, I’ll explain, and see if I can’t convince you.
Gone are the days when you might include jQuery and a couple of other JavaScripts on the frontend. Now entire sites are being built via JavaScript. Even if you’re not embracing the whole JAMstack thing — and like anything else, there are downsides and reasons not to: pick the right tool for the job — building a modern website is getting more and more complicated.
Websites have morphed from design-driven “Digital Brochures” to developer-driven full-on customized applications, as discussed in the A Pretty Website 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 article from the point of view of a frontend developer who uses a variety of third party and custom JavaScripts to progressively enhance and as a compliment to the HTML & CSS on the website
Link A Token Example
So for example, let’s say we’re building our webpage, and we need to include jQuery and a half-dozen other JavaScripts, some that depend on jQuery and some that don’t.
Then we have our own custom JavaScript that we sprinkle around. It’s too small to justify putting it into a separate .js file, so we inline it to do things like click handlers, and so on.
Okay, no problem, 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 render blocking.
Alright then, let’s put the <script> tags at the bottom of the webpage 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 render block. Now our webpage randomly 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 early, and put the rest down before our </body> tag, and take the performance hit of render blocking. But now one of our scripts that we want to use in our inline JavaScript isn’t working, because it depends on jQuery.
Sigh. So we move jQuery up into <head>, too, along with this script. Everything finally works, but Google PageSpeed Insights hates us, it’s frankly a mess, and is pretty fragile if we start introducing more complexity to the mix.
Calgon, take me away!
Link It’s all about Dependencies
This scenario is a small-scale example of how things can get out of hand even in a pretty simple setup. When we start adding more third party scripts, and more custom JavaScript of our own, it breaks down pretty quickly.
What we really need is something that will manage our dependencies for us — ensuring that jQuery is loaded before any JavaScripts that need it, for example — and will take care of loading our JavaScripts in a performant manner.
That’s precisely what SystemJS 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 asynchronously, so we won’t be render blocking, and we can use it both on the frontend in our web browser, and on the backend in our NodeJS code. Cool.
Now, this is the JavaScript landscape we’re talking about, so there many ways to skin the same cat. In the past, I’ve used RequireJS for this purpose, and it’s served me well. However, SystemJS is tracking the JavaScript ES6 spec, so we’re really future-proofing 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 worrying about dependencies, debugging weird JavaScript errors, and more time building your website on a solid, scalable foundation. Plus, you can write your JavaScript code in the fancy ES2015 format even if your browsers don’t support it.
What is this sorcery? I won’t get too deep into the nerdstuff here. We’re not going to use jspm (indeed, I’ve been reducing the package managers I use) or worry about any of SystemJS’s more esoteric features, but rather just focus on the practical application of SystemJS on a website. A good read is the ES2015: A SystemJS Odyssey article if you want more.
Tangent: SystemJS can actually do a whole lot more, even doing super cool stuff like runtime dynamic transpiling of your JavaScript from things like TypeScript, ES6 JavaScript, and so on as they are loaded. It also supports the loading of all of the major JavaScript module formats such as ECMAScript Module, CommonJS, Asynchronous Module Definition, and old skool Globals shims… so you don’t have to know or care how they were written. Until there is full browser support, you’ll still need to transpile any ES2015 JavaScript, either at runtime via a SystemJS Babel plugin, 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 decided you might as well finish what you started. So let’s have a look at how this will all work.
First of all, we’ll allow you one external script tag on your web page, something that looks like this:
<script src='/js/system.min.js'></script>
Looks pretty normal, this just loads in the systemjs.min.js script. Then we need a way to tell SystemJS where our JavaScripts are located, and we can do that either in a separate 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 config settings can go anywhere; all that’s required is that SystemJS is loaded already, so that the System object is available 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 simple setup. You tell SystemJS what the baseURL is for where all of your JavaScripts are located, and then in the paths you just map the name you want to refer to the JavaScript by partial path name (used in conjunction with baseURL) or to the full URL to the JavaScript itself.
Now let’s say we have a bit of code that depends on VueJS, 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 SystemJS we want vue loaded, and it will go off and asynchronously load it and all of its dependencies, then execute the callback function 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 worry about the order in which our <script> tags are listed. We just say what we need, and it gets loaded. Since SystemJS knows all about various module formats, 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 SystemJS goes to load this main.js JavaScript, it knows we want all of those modules imported, and it’ll load them for us as well. Sweet.
This approach of keeping JavaScript modules in separate files that are loaded on-demand works better for http2 than globbing everything together into one massive JavaScript file. And it makes it logically easier to manage as well.
The website you’re reading right now uses SystemJS to manage all of its JavaScript dependencies, and load them asynchronously as needed.
This structured dependency approach is much more scalable than random sprinkling your HTML pages with <script> tags. It works on the frontend in-browser, and on the backend in NodeJS. And it’s where the future of JavaScript lies as well.
So get on board now…