What is tree-shaking

Let’s take a look at the original idea of Tree-shaking





The image above illustrates the meaning of tree-shaking. In this article, tree-shaking can be understood as “shaking” our JS files with tools to “shake” the code that is not used. It is a category of performance optimization. Specifically, in a WebPack project, there is an entry file that corresponds to the trunk of a tree, and the entry file has a number of dependent modules that correspond to branches. In practice, you rely on a module but only use some of its functionality. With tree-shaking, modules that are not in use are shaken off to remove useless code.



Tree-shaking was implemented earlier by Rich_Harris’s rollup, and was later added to WebPack2. Google Closure Compiler did something similar earlier. The effect and use of the three tools are different. You can learn how to use them through the official website documents. The effect comparison of the three tools will be introduced in detail later.


Ii. Principle of Tree-shaking

The essence of tree-shaking is to eliminate useless JS code. Dead code Elimination, which is widely used in traditional programming language compilers, allows the compiler to determine that certain code doesn’t affect output at all and then eliminate it. This is called dead code elimination (DCE).

Tree-shaking is a new implementation of DCE. Unlike traditional programming languages, Javascript is mostly loaded over a network and then executed. The smaller the file size is loaded, the shorter the overall execution time is. Makes more sense for javascript.

Tree-shaking is different from traditional DCE, which eliminates code that is impossible to execute, whereas tree-shaking focuses on eliminating code that is not used. Here’s a look at DCE and tree-shaking in more detail.


(1) First look at DCE elimination



Dead Code generally has the following characteristics

• Code will not be executed and is unreachable

• The results of code execution are not used

• Code only affects dead variables (write only, don’t read)


The code in the red box below is a dead code that satisfies the above characteristics

Figure 4.

In traditional compiled languages, the compiler removes Dead Code from the AST (abstract syntax tree). Who does the DCE in javascript?

It’s definitely not the browser that does DCE in the first place, because when our code is sent to the browser, we’re not talking about eliminating the code that can’t be executed to optimize, so it’s definitely the steps before sending it to the browser to optimize.

In fact, it is not the three tools mentioned above, Rollup, Webpack, CC, but the well-known code compression optimization tool Uglify, Uglify completed javascript DCE, the following through an experiment to verify.


All of the sample code below can be found on github, with the stamp ❤

Github.com/lin-xi/tree…


Package the code in Figure 4 with rollup and WebPack, respectively

Figure 5

In the middle is the result of rollup packaging, and on the right is the result of WebPack packaging

As you can see, rollup eliminates the useless foo and unused functions, but still leaves the code that will not be executed, while WebPack completely keeps all the useless and unused code.


Package the code in Figure 4 with rollup + Uglify and WebPack + Uglify, respectively

Figure 6.

The configuration file is in the middle, and the results are on the right

As you can see on the right side of the final package, the code that can’t be executed is removed, and the results are in line with our expectations.


(2) Take another look at the tree-shaking elimination method

As mentioned earlier, tree-shaking is more concerned with eliminating useless modules, modules that are referenced but not used.

Consider why tree-shaking has become popular in recent years. While the concept of front-end modularity has been around for many years, tree-Shaking’s elimination principle relies on the modular features of ES6.

ES6 Module Features:

  • Appears only as a statement at the top level of the module
  • Import module names can only be string constants
  • Import binding is immutable

The FOUNDATION for Tree-shaking is that ES6 module dependencies are determined, independent of runtime state, and can be reliably analyzed static.

The so-called static analysis is not to execute the code, the code literally analysis, before ES6 modularization, for example, we can dynamically require a module, only after the execution of the reference module, this can not be optimized through static analysis.

This is an important consideration in the design of ES6 Modules. It is also why CommonJS is not used directly. It is on this basis that tree-shaking is possible. This is why both Rollup and WebPack 2 use ES6 Module syntax for tree-shaking.


Let’s look at it in detail by example

Procedure oriented programming function and object oriented programming are the most commonly used programming mode and code organization mode of javascript, from these two aspects to experiment:

  • Function elimination experiment
  • Class elimination experiment

So let’s do the function elimination experiment

The get method is not used in the utils, and what we expect is that the get method will eventually be eliminated.

Note that UgLIFy does not currently do DCE across files, so uglify cannot be optimized in this case.

Let’s start with rollup’s packaging results

Exactly as expected, there is no GET method in the final result

Take a look at the Webpack results

Also as expected, there is no GET method in the final result

You can see that rollup packaging results are more optimized than WebPack

In the function elimination experiment, both Rollup and Webpack passed, as expected


Let’s look at the class elimination experiment

We added a reference to menu.js, but we don’t actually use any of the methods and variables of Menu in the code, so we expect that the content of menu.js in the final code will be eliminated

main.js

menu.js

Rollup packages the results

The package contains the entire menu.js code

Webpack packages the results

The package also contains the entire menu.js code

Class elimination experiments, rollup, WebPack all failed to meet expectations

What happend?

This is not what we expected, is it? Why is that? If useless classes can’t be eliminated, is it tree-shaking? At one point, I suspected that my demo was wrong, but after a variety of Internet searches, I realized that my demo was right.

Here are some answers from the rollup core contributors:

Figure 7.

  • Rollup only handles functions and top-level import/export variables. It does not eliminate unused class methods
  • The dynamic nature of javascript makes static analysis difficult
  • The code below Figure 7 is an example of a side effect. If run or jump is removed during static analysis, the program may report an error when it is running


Here’s another example of why you can’t eliminate menu.js, such as in this scenario

function Menu() {
}

Menu.prototype.show = function() {
}

Array.prototype.unique = function() {// remove duplicate elements from array}export default Menu;
Copy the code

If you remove menu.js, the extension to Array will also be removed, affecting functionality. So you might ask, can’t rollup WebPack distinguish between proptoType that defines Menu and ProptoType that defines Array? Of course it’s distinguishable if I write it this way, but what if I write it this way?

function Menu() {
}

Menu.prototype.show = function() {
}

var a = 'Arr' + 'ay'
var b
if(a == 'Array') {
    b = Array
} else {
    b = Menu
}

b.prototype.unique = function() {// remove duplicate elements from array}export default Menu;
Copy the code

This is a code that static analysis cannot analyze, and even if you could analyze it statically, it would be difficult to analyze it correctly and completely.

For more discussion of side effects, see this



Tree shaking class methods · Issue #349 · rollup/rollupgithub.com


Tree-shaking is good for functions

Functions have fewer side effects, top-level functions are relatively easy to analyze, and Babel defaults to “use strict” mode, reducing the dynamic access to top-level functions and making them easier to analyze


The first three tools we talked about, Rollup and WebPack, didn’t do very well. What about the Closure Compiler?

Packing the code in the example with CC gives the following results:

Oh, my God. Isn’t that what we’re looking for? The result is a perfect elimination of all the useless code, and the output is very sexy

Closure Compiler, Tree-shaking results are perfect!

But don’t get too excited too soon. To get such a perfect result, there is a condition, which is the intrusive constraint specification of CC. You have to add code like this to your code. See the red box

Annotating JavaScript for the Closure Compiler

This is annoying because Google’s Closure Compiler is written in Java and is not compatible with our Node-based build libraries (although it does seem to have a NodeJS version). Closure Compiler is also cumbersome to use, so while it works well, it is difficult to apply to a project and can cost a lot to migrate.


Having said all this, to sum up:

The tree-shaking of the three tools is limited and conditional on the elimination of useless code and modules. Closure Compiler is the best, but it’s hard to make it compatible with our everyday Node-based development streams.

Tree-shaking is of great significance for the Web. It is an ideal world of extreme optimization and another ultimate ideal for front end evolution.

Ideal is beautiful, but it is still in the development stage, it is still more difficult, there are various aspects, and even there seem to be unsolved at present

But we should still believe that new technologies can bring about a better front-end world.

Optimization is an attitude, not because of small and not for, not because of difficult and not attack.


Limited knowledge, if wrong, please do not hesitate to correct, thank you


The next article will continue the tree-SHAKING performance optimization practice-Practice article






The sample code in this article can be found at github, with the welcome stamp ❤

lin-xi/treeshakinggithub.com