JavaScript Starter Architecture, Part 3: Webpack Browser List, Reduction of Transpilation and Polyfill

March 06, 2018 0 Comments

JavaScript Starter Architecture, Part 3: Webpack Browser List, Reduction of Transpilation and Polyfill

 

 

Lucas van Valckenborch [Public domain], via Wikimedia Commons

Continuing my multi-part series chronicling my personal journey of crafting an architecture for JavaScript on a traditional website, today I turn to the fine tuning of webpack and clearing up the missing polyfill piece.

For reference, the previous articles are here:

In part 2, I iterated over mechanisms for loading a polyfill for your website, and today I am finalizing putting them into the starter architecture and loading them as part of my webpack.

The key to this particular article is the Browser List — thank you to whomever started this module as a way of standardizing the way our tools can identify a list of browser versions. For this example, we’re going to say we will support the last two versions of every browser, except IE we will not support anything older than 11.

Babel’s env preset does not appear to honor reading from package.json file, so instead we have to update our .babelrc directly:

{
"presets": [
[
"env",
{
"debug": true,
"targets": {
"browsers": [
"last 2 versions",
"not ie < 11"
]
},
"useBuiltIns": true
}
]
]
}

Our original .babelrc file contained {"presets": ["env"]}, but now it is more complicated. An important note is that the presets value became a list-of-lists to add the options dictionary to “env” definition. I missed that fact for a time, and it drove me nuts. This configuration change, using the Browser List format, tells Babel the exact browsers we want to support: The last two versions of all browsers, but no Internet Explorer version less than 11.

By providing those browser lists, Babel can now limit the amount of transpilation it does to match what those specific browsers lack. For example, if you only specify environments which already have native support for arrow functions, Babel will not need to transpile them.

For our polyfill needs, we must do an npm install — save-dev babel-polyfill, and add import "babel-polyfill"; to our entry script. In our example architecture case, our entry script is our site.js file. With the targets listed above, specifying "useBuiltIns": true in .babelrc,and importing babel-polyfill, Babel will calculate only those polyfills needed by the selection of browsers. If there is a polyfill available which is already supported natively by all of the browsers, it will not include them. Without the target list and without "useBuiltIns": true, Babel will include all available polyfills.

I include the "debug": true as a way of seeing what Babel is doing when you execute npm run pack. If you toggle useBuiltIns you can see it includes all pollyfills which exists. With it set to true, it will show you which ones it is including along with which of your specified browsers require it. For example, the following line tells you that the es7.array.includes polyfill is being included because Android 4.4.3 and IE 11 lack this functionality:

es7.array.includes {“android”:”4.4.3",”ie”:”11"}

When you look at the long list of polyfills being includes (up to 90KB!), you will probably see large chunks of things that you know you do not use. You can add an "exclude": [] list of names to the configuration to have those, well, excluded. The downside is that there is no way to tell it to exclude all of es6.math.* — you have to list each and every one of them.

If you know you only require a few, it might be simpler to just import the ones you need in your entry script. Keep in mind that you only need to load polyfills in your site.js, and as long as it is the first script you load and execute, then all other scripts on your site will have access to these polyfills.

Under my current plan for JavaScript Architecture, this works well because your site-wide code (site.js) and the code that runs directly on groups of pages or single pages all co-exist together.

For your reference, I have updated the Sample Architecture on GitHub.

Above is the direction I am taking this starter architecture I am developing here with you. However, on my work site, because I care very much about the site load time, and I do not want to load a bunch of code which is not necessary on the page for most of my visitors, I’m doing something different. Here’s what I am doing there:

<script>
// <![CDATA[
(function() {
var myNav = navigator.userAgent.toLowerCase();
if (!Object.entries || !Array.from) {
document.write('<script src="/js/polyfill.min.js"></scr'+'ipt>');
}
})();
// ]]>
</script>
<script src="/js/site.js"></script>

Why do I use document.write? Very simply because, for those who choose to use a browser lacking modern JavaScript support, I need to load these polyfills before my site.js executes. The site.js expects the functionality to be in the JavaScript environment when it executes.

What is polyfill.min.js? That is the Minified version of the full set of polyfills provided by babel-poly. You could use any polyfill sufficient to cover your needs. You could even have it include from polyfill.io. The choices are many! This is one of the areas that I will be improving upon as we use this new architecture on our site.

I am still learning and still developing my thoughts around this architecture, and my real-life site.js for my work site continues to grow and mature. Soon I will bring you some of the changes I’ve made there and I will unveil my plans for the “groups of pages” and “single page” JavaScript code in this architecture.


Tag cloud