ES6 modules, now in a browser near you

There are some news and changes since I last visited ES6 modules. I started hearing more and more about WebPack and how it did the one thing that Rollup doesn’t…. it splits the code in different bundles based on the overall size of your Javascript files.

The latest news is that modules are now supported in browsers so packaging them may or may not be as necessary any more.

ES6 modules defined

In ES6 each module is defined in its own file. The functions or variables defined in a module are not visible outside unless you explicitly export them. This means that you can write code in your module and only export those values which should be accessed by other parts of your app.

ES6 modules are declarative in nature. To export certain variables from a module you just use the keyword export. Similarly, to consume the exported variables in a different module you use import.

Rollup

Things haven’t changed since the last time I worked with Rollup so I’ll use the same example I used then.

The first part of the example is the notional image manipulations. We load the image using a promise and resolve it on load and reject with a new error. The other functions just log their results to console.

Using the export keyword tells the parsers that we will import the function from other scripts as we’ll do later in our main script.

Our main script uses the import keyword to bring the exported functions from the image-manip library into the current file. We then use it to create a function without having to redefine the functions, we just use them.

How we run the command depends on the tooling we have set up. We can run Rollup from the command line using a command like the one below. This command assumes that there is a rollup.config.js in the directory where we run the command.

rollup -c --output bundle-2.js # --output is equal to dest

We can also configure NPM to run our Rollup build as a script. In the example below we run the command using npm run build which will then trigger the build sing the configuration available in the same directory.

The last version is a Gulp task to handle transpilation to ES5 and run Rollup to create the bundle. This is pretty close to the configuration file but split differently to accommodate the two-step process. Soon creating bundles for browsers will become easier because we won’t need the transpilation process… browsers are beginning to support modules natively.

Webpack

Webpack is the 500 pound gorilla when it comes to compressing and bundling web content. It works on both CSS and Javascript to take over a lot of things that we can and should do in our build process.

That said it does some things that no other build system does. It works to create CSS and Javascript bundles from the same configuration (assuming that it is properly configured), it will warn you if any of your bundles are over 250Kb which prevents the bundles from slowing down page loading.

I think it’s too big a tool that tries to do too much but, again, it seems that everyone is using it so you may have to use it whether you like it or not.

We will only concentrate on Javascript bundling, code splitting and shaking. I will not use Webpack for handing images and S/CSS because I don’t think we should be converting it all to Javascript. Think about it, we load over 1MB of images into our pages, and a couple hundred KB of CSS and we will convert all those images to strings just so we can feed them to Javascript and then place them back into their correct locations? Thanks but no thanks… I would rather use traditional strategies built around Gulp and it’s ecosystem and not split it into something completely different

In order to run the files we need to modify the project’s package.json to add scripts that will run Webpack. There are ways to incorporate the tool into Gulp and Grunt build processes, that is left as an exercise to the reader.

{
"scripts": {
"build": "webpack"
},
}

One last thing to consider. The configuration presented above will bundle everything together and that may not always be desirable.

We can modify the Webpack configuration script to bundle all our vendor assets into separate chunks.
The modified script looks like this:

By providing multiple entry points we make it easier to use the CommonsChunkPlugin to bundle together our vendor (normally third party) libraries. In the example we’re bundling moment but this may also be an array of our vendor dependencies.

Why do we need to convert everything to Javascript?

My biggest peeve with Webpack and the reason why I’ve only shown how to work with Javascript is that I’m still not convinced everything should be done in Javascript.

What is the difference between bundling all CSS into Javascript so we can be sure that we’re only feeding the user the CSS needed for a page and using UNCSS and Critical to inline and deliver only the CSS needed for a page or a screen in an app?

Same thing with images; Is it worth or even necessary to convert all images over a given size into Base64 strings just so we can inline them in Javascript or is it better to use responsive images (with all the associated pain in the ass they entail) and build the responsive images from a script or a build file?

Granted, everyone has an opinion on this subject. Mine is that I don’t think everything in JS is the answer to all our development issues.

Modules natively in browsers

Rollup and Webpack are popular because, until recently, there was no way to work with modules directly in web browsers. Even though ES6 is almost 2 years old we had no browser that supported modules natively.

That is changing. Native support for modules is starting to come out in browsers… it’s still experimental in most of them but it’s a good sign that they will come into release version of the browsers soon. The current list of supported browsers:

The last concern when working with native module implementations is how to handle older browsers. Most modern browsers have repurposed the type attribute of the script element: If it’s value is module the JS engine will treat the content as a module with different rules than those for normal scripts.

There is a lot more to learn about native module implementation. Some of the things that caught my attention when researching modules:

They only run once per page no matter how many times you load it.

Modules and script modules never block rendering. They always run as if the defered attribute was set in the calling script tag. The defer tag means that the script will execute after the content is downloaded but before the DOMContentLoaded event is fired.

Modules and inline (script) modules can use async attribute meaning that they can be made to load without blocking HTML rendering but it also means that you can no longer guarantee execution order. If the order your scripts run in is important rely on the defered attribute discussed earlier.

Must use a valid Javascript Mime Type or it will not execute. In this context, a valid Javascript Mime Type is one of those listed in the HTML Standard: