Distribute transpiled Javascript or not (ES5 or ES6) according to browser with Webpack

Configure Webpack to deliver transpiled code or not, depending on whether the browser supports ES5 only or supports ES2015 (ES6).

Currently most browsers support ES6. However, a large number of developers continue to distribute transpiled Javascript code, in ES5, to ensure compatibility with all browsers.

But we must ask ourselves, is that the best strategy? The truth is, no. Transpiling is a necessary evil in many cases, but we can avoid it for browsers that are ready, speeding up the download and processing of the code, which will improve the performance of web sites and applications.

In this article we want to explain the configuration of Webpack so that you can distribute the version of the code that best suits each browser in your frontend application. Those that are compatible with ES6 will receive the original code and those older browsers that only support ES5 will receive the transpiled code.

Why it’s not a good idea to transpile for all browsers

When transpiling from ES2015 (ES6) to ES5, the code is transformed, so that it is compatible with all browsers. In this transformation, it happens that the code increases in size, since features now available natively by browsers have to be emulated.

That increase in size is noticeable. It’s not a few KB, but can sometimes be almost twice the size. For example, in a tiny website, a Javascript bundle written in ES6 can weigh around 180 Kb. Once transpiled it can end up weighing 334 Kb. With the small project that I have built to illustrate the article, we are going to demonstrate it. In this image you can see the sizes of the bundles, with the same transpiled and non-transpiled code.

Now imagine in terms of large applications, where we can have several hundred Kb difference. You will realize how important it is to offer each browser an optimized code according to its capabilities. But also, think that download time is not the only price to pay for stacking. If the browser has to process twice as much Javascript code to do the same, performance will suffer as well.

How to configure Webpack to generate two bundles with different Babel configurations

Well, then we are going to approach the practice, to first get the two bundles that we want as a response to the code production process.

  • Bundle with the original code (not transpiled, in ES2015)
  • Bundle with transpiled code (ES5)

We will perform this task with Webpack. We will explain the procedure below, but it is important that you know the bases of this tool in advance, for which we recommend reading .com.

To achieve this, we are also going to use Babel, who is the one who actually performs the transpilation. We must therefore produce two configurations for Babel, one with transpilation and the other without transpilation.

Note: In our example we will have a configuration with transpilation and another without transpilation, but in many cases we could really have two configurations of babel with transpilation, since we could use constructions like async/await, which are only available in ES7, and therefore outside of ES7. current reach of browsers. So we could have code transpiled to ES5 for older browsers and code transpiled to ES6 for modern browsers. In any case, however you do it, they are still two different configurations of Babel.

Webpack does not allow implementing in the same configuration file (webpack.config.js) two different configurations for Babel, so we have to do the work in different configuration files.

webpack.config.js for configuration with transpilation

Let’s start by taking a look at the Babel configuration file, for code transpiled to ES5. It might look something like this:

const HtmlWebpackPlugin = require(‘html-webpack-plugin’); module.exports = { module: { rules: }, plugins: } Note: remember that to do the transpilation correctly, you need to install various Babel dependencies, in addition to making the .babelrc and other details that we have explained in the article on . Also in this manual we have explained other things used in the previous example, such as the .

Webpack.config-es6.js for the non-transpiled bundle

On the other hand we will have a special configuration file, which does not perform transpilation. This file is quite similar, with the difference that we don’t configure Babel in it.

const HtmlWebpackPlugin = require(‘html-webpack-plugin’); module.exports = { output: { filename: ‘main-es6.js’ }, plugins: }

Note: In your project this could be a configuration that transpiles from ES7 to ES6, if required by your Javascript code.

Notice here an important detail: The template of this second script, in the HTML Webpack Plugin instantiation, is taken from dist/index.html, instead of src/index.html. It is so that when injecting the code of this second bundle, the script is placed on top of the HTML file already generated in the previous webpack.config.js configuration.

The execution sequence and the creation of the template will be:

1) Webpack is first executed with the webpack.config.js configuration

1.1) src/index.html is taken as template and script from the first bundle is injected.

2) Webpack is executed with the configuration webpack.config-es6.js.

2.2) The HTML file generated in the previous step (1.1) is taken as template and the bundle is injected

npm scripts to invoke the configs

These two configurations have to be executed in different calls to Webpack, the first and the second, in that order. To summarize this task we can create some npm scripts, in the package.json, like these:

“scripts”: { “build:es5”: “webpack –mode production”, “build:es6”: “webpack –mode production –config webpack.config-es6.js”, “build”: “npm run build:es5 && npm run build:es6”, },

  • The first script “build:es5” runs Webpack with the default configuration we have in webpack.config.js
  • The second script “build:es6” runs Webpack with the non-transpiled configuration, which we have in the webpack.config-es6.js file.
  • The third “build” script has the calls to both scripts seen above, so you can get both executed with a single command.

Module and nomodule

This point is important, since once we have two files with code aimed at browsers that support ES6 and those that only reach ES5, we must teach each web client to obtain the most suitable script for it.

Achieving this is very simple thanks to the script tag, in which we can add specific attributes to tell the browser which is its script file to load. The type=”module” attribute will tell browsers that that code is written with ES6 (ES2015). While the “nomodule” attribute will indicate that that code is only intended for browsers that don’t understand ES6.

Note: If you want more detailed information on this practice to deliver the proper Javascript code depending on browser compatibility, we recommend reading the .

The problem that can be presented to us at this point, derived from the use of Webpack, is that the script codes are injected into the page through the use of the HTML Webpack Plugin. This plugin, from home, is not prepared to be able to place specific attributes to SCRIPT tags, so we have to use a plugin that we have not talked about yet.

Using the Script Extension for HTML Webpack Plugin

The npm package “script-ext-html-webpack-plugin” is a Webpack plugin (although more specifically we should say that Script Extension for HTML Webpack Plugin is an HTML Webpack plugin. Sounds like a tongue twister!). It is used to arbitrarily add any type of attribute in the SCRIPT tag placed by the HTML Webpack Plugin.

In order to use this plugin we first have to install it.

npm install -D script-ext-html-webpack-plugin

Once installed, its use is quite similar to . We first have to do the require and then instantiate the plugin in the “plugins” array of the Webpack configuration.

So let’s see how the two configuration files used for this Webpack 4 example would look, once we integrate the use of the Script Extension for HTML Webpack Plugin.

config.webpack.js file

You can look at the require, second line of code, along with the “new ScriptExtHtmlWebpackPlugin”. Everything else is exactly the same.

const HtmlWebpackPlugin = require(‘html-webpack-plugin’); const ScriptExtHtmlWebpackPlugin = require(‘script-ext-html-webpack-plugin’); module.exports = { module: { rules: }, plugins: }) ]}

Remember that with the previous configuration script we are defining the code for browsers that only support ES5. Therefore, it will be inserted into the “nomodule” attribute in the script tag.

webpack.config-es6.js file

This is the configure script to produce the ES6 code, for modern browsers.

const HtmlWebpackPlugin = require(‘html-webpack-plugin’); const ScriptExtHtmlWebpackPlugin = require(‘script-ext-html-webpack-plugin’); module.exports = { output: { filename: ‘main-es6.js’ }, plugins: }

In this case we will use a specific configuration of ScriptExtHtmlWebpackPlugin, in which we are indicating to place the type=”module” attribute.

Injection produced by this Webpack configuration

With these changes, we will get script tags like the following. You will be able to see the placement of the attributes for module and nomodule.

conclusion

We have seen how to generate script code with various flavors, adapted or not to the latest features of Javascript, ES2016 and ES5. We have also seen how to inject these scripts so that each browser downloads and uses the most appropriate one according to its compatibility.

It’s been a bit more complex practice than we’ve seen before in the , but one that will prove very useful for optimizing modern sites and apps. It will undoubtedly be essential in complex web applications, with a large amount of Javascript adapted to take advantage of the latest advantages of the language.

See also  Change font color of a form text field with Javascript
Loading Facebook Comments ...
Loading Disqus Comments ...