Tree shaking in Javascript
Tags: javascript, treeshaking, webdev, discuss
Published: 2022-09-21 12:09:04

JavaScript is an expensive resource to process. It needs to be parsed, compiled, and then finally executed. This makes JavaScript more expensive to process.

There are techniques to improve JavaScript performance. Code splitting is one such technique that helps in improving performance by partitioning JavaScript into chunks, and serving those chunks to only the routes of an application that need them.

Though this technique works, it doesn't address a common problem of JavaScript-heavy applications, which is the inclusion of code that's never been used. Tree shaking helps in solving this problem.

Tree shaking is a method of optimising our code by eliminating any code from the final bundles that isn’t actually being used.

Let’s say we have a `stringUtils.js` file with some operations we may want to use in our main script.
```js
export function capitaliseFirstLetter(word) {
    return (word.charAt(0).toUpperCase() + word.slice(1))
}

export function getInitials(name) {
    if (name) {
        return name
            .split(' ')
            .map((n) => n[0])
            .join('')
    }
    return ''
}

export function truncateString(string, len) {
    const truncatedString = string.substring(0, len)
    return `${truncatedString}...`
}
```
In our main script, we may only ever import and use the truncateString() function.
```js
import { truncateString } from "./stringUtils";

truncateString('sample string', 10);
```
After the bundling, we will see that all functions from the file are included in the final bundle, even though we only imported and used the `truncateString` function.

But with tree shaking enabled, only what is imported and actually used will make it to the final bundle.

Although the concept of tree shaking has been around for a while, it has only been able to work with Javascript since the introduction of ES6-style modules. This is because tree shaking works iff the modules are `static`.

Before going forward, let's get some understanding on `static` and `dynamic` modules.  When you use a module as static module, you are instantiating and using it for across the application. So, even though a component might not be using it, there is still some performance cost associated with it because anytime we try to access that, the namespace of that module will be available. Dynamic module imports are very similar to static module imports, but the code is loaded only when it is needed instead of being loaded instantly.

Before the introduction of ES6 modules, we had CommonJS modules which used the `require()` syntax to import the modules. These modules are `dynamic`, meaning that we are able to import new modules based on conditions in our code.
```js
var myDynamicModule;

if (condition) {
    myDynamicModule = require("foo");
} else {
    myDynamicModule = require("bar");
}
```
The dynamic nature of the CommonJS modules meant that tree shaking couldn’t be applied because it would be impossible to determine which modules will be needed before the code actually runs.

In ES6, a completely new syntax for modules was introduced, which is entirely static. Using the import syntax, we cannot dynamically import a module. We need to define all imports in the global scope, outside of any conditions. This new syntax allows for effective tree shaking, as any code that is used from an import can be determined even before running the code.

Tree shaking in most of the good code bundlers is pretty good at eliminating as much unused code as possible. For example, modules that are imported but not used are eliminated.

Some libraries also suggest to use the submodules for imports instead of the module which prevents the bundling of unused code. Here is one such example from `@vueuse`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fp1nas08xizhdzlmco1p.png)

Though, tree shaking helps to eliminate the unused code but it doesn’t solve the problem of unused code entirely.

## Things to consider while using Tree shaking:

Tree shaking cannot tell on its own which scripts are side effects, so it’s important to specify them manually.

**Side effect** is a code that performs some action when imported but not necessarily related to any export. A very good example of a side effect is polyfill. [Polyfills](https://developer.mozilla.org/en-US/docs/Glossary/Polyfill) typically don’t provide an export, but rather added to the project as a whole.

Tree shaking is typically implemented via some code bundler. Each code bundler has a different way of enabling tree shaking. If you’re using webpack, you can simply set the mode to production in your `webpack.config.js` configuration file. This will, among other optimisations, enables tree shaking.
```js
module.exports = {
    ...,
    mode: "production",
    ...,
};
```
To mark any file as side effect, we need to add them to our package.json file.
```js
{
    ...,
    "sideEffects": [
        "./src/polyfill.js"
    ],
    ...,
}
```

Modules that are being used as side effects cannot be tree shaken because they don’t have imports and exports.
But, it is not important to be a module to have side effects. Take the following code as an example:

```js
// src/sideEffect.js
export const foo = "foo";

const sideEffectFn = (text) => {
  fetch("/api");
  return `${text}!!`;
};

export const bar = sideEffectFn("Hello");
// src/index.js
import { foo } from "./sideEffect";

console.log(foo);
```
The bar variable will trigger a side effect as it is initialised. Webpack will realise this and will include the side effect code in the bundle, even though we aren’t using bar at all.

In this article, we covered tree shaking in general, example of its usage using Webpack and how to make common patterns tree-shakable. 

I hope this article helped all of us understand tree shaking easily. To learn more, visit the [MDN docs](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking).

Love reading about Javascript, Web optimisations, Vue.js, React, and Frontend in general? Stay tuned for more.

Wanna connect? You can find me on [LinkedIn](https://www.linkedin.com/in/milek-agrawal-b78008185/), [Twitter](https://twitter.com/milek_agrawal), [GitHub](https://github.com/milekag01).