Reduce your resource size with CSS and JavaScript Minification<!-- --> | <!-- -->Web Performance Tips

Reduce your resource size with CSS and JavaScript Minification

Minification is one of the most common techniques web developers use to reduce the their app's resource sizes.

Minifiers are build tools that statically analyze your JavaScript and CSS resources and apply techniques for reducing their size by:

  1. Dead Code Elimination: Identifying unused codepaths that can be completely removed.
  2. Renaming and Reformatting: Of the code that cannot be removed, safely rename variables and remove extraneous whitespace and formatting

It's highly likely you are already using a minifier if you are using popular build tools like Webpack.

Why Minify Resources?

Resource size has a direct impact on both network and CPU utilization. Larger resources generally take longer to transfer over the network and they also take longer to process on your user's CPU once they are transferred.

Since web application performance is particularly sensitive to resource size, a performance-minded web developer should ensure no code being delivered to end users is extraneous or unused.

Tools Available

Some common minification tools include:

  • Terser, which is automatically enabled for Webpack's production mode.
  • Closure Compiler which is an aggressive minifier and imposes constraints on how your app is authored to produce highly optimal bundles
  • ESBuild minifies both JavaScript and CSS resources
  • CSSNano, which is utilized in webpack's CSSMinifierPlugin

Dead Code Elimination

For JavaScript, a minifier will analyze code paths and detect if there are sections that are certainly not executed. This is called dead code elimination.

Here's an example:

const condition = false;

if (condition) {
    doSomething();
} else {
    doSomethingElse();
}

Dead code elimination can statically determine that condition will always be false. As a result, it can save precious bytes in our final payload, by transforming the above code into simply:

doSomethingElse();

In CSS, a minifier can remove extraneous properties specified in source files. Here's an example:

.class-name {
    padding: 50px 50px 50px 50px;
}

Dead code elimination for CSS determines that 3 out of 4 50px definitions specified are extraneous. As a result, after dead code elimination, our CSS file would look like this, saving previous bytes in our CSS file:

.class-name {
    padding: 50px;
}

Tree Shaking

Tree shaking is a specific form of dead code elimination, and it's specific to the ES6 import and export syntax frequently used in modern web apps.

Tree shaking techniques involve statically analyzing the import and export graph to determine if unused exports can be pruned from the final payload.

Consider the following example files: functions.js and app.js:

// File functions.js

export function doSomething() {
    console.log('Do Something')
}

export function doSomethingElse() {
    console.log('Do Something Else');
}

// ... other functions
// File app.js

import { doSomething } from './functions.js';

doSomething();

In this example, while functions.js exports many functions, only doSomething() is actually pulled in via import. As a result, tree shaking will ensure only the doSomething() function is included in the final payload delivered to the user.

In general, ensure the following are turned on for proper tree shaking:

  • If using Webpack, ensure package.json property "sideEffects": false is specified
  • Ensure you are using ES6 import and export instead of CommonJS require and module.exports
  • If using TypeScript, ensure your module is set to es6 or higher

Renaming and Reformatting

For the code that cannot be statically eliminated, minifiers ensure it's as small as possible. This is achieved through Renaming and Reformatting. You may also see this called Mangling or Compression.

There are a variety of opportunities for a minifier to apply this technique.

For example, in this JavaScript code:

// functions.js
export function doSomething() {
    const randomValue = Math.random();

    return randomValue;
}

// ...

// app.js
import { doSomething } from './functions.js'

doSomething();

The minifier can safely rename and reformat this code to produce the following:

function a(){return Math.random()}a()

In this way, the minifier can save precious bytes by:

  1. Removing any extraneous whitespace characters we inserted in source code for readability
  2. Rename the function from doSomething to a since a is smaller
  3. Remove the extraneous variable assignment to randomValue, as it can be safely removed without affecting the code's logic

Note: The import graph is flattened by the bundler / build tool and is not part of minifier's responsibilities.

For CSS Code:

.class-name {
    font-size: 16pt;
}

A minifier would produce:

.class-name{font-size:16pt}

Our byte savings would arise from reformatting of the CSS to eliminate extraneous whitespace.

Conclusion

While most production web applications are likely already using minification, we have covered the why and how of these tools.

If you happen not to be using a minifier, you should turn one on ASAP to take advantage of this easy and effective optimization!

Learn about how functions minify better than ES6 classes next!

That's all for this tip! Thanks for reading! Discover more similar tips matching Beginner and JS Optimization.