Reducing JavaScript Payload size using Tree Shaking Technology
- Reduce JavaScript Payloads with Tree Shaking
- Translator: Fundebug
To ensure readability, free translation rather than literal translation is used in this paper. In addition, the copyright of this article belongs to the original author, and translation is for study only.
Xiaobian recommendation:FundebugFocus on JavaScript, wechat applets, wechat games, Node.js and Java online bug real-time monitoring. Really a good bug monitoring service, many big companies are using.
Nowadays a web app can be very bulky, especially JavaScript code. In mid-2018, HTTP Archive calculated that the average transfer size of JavaScript files on mobile was nearly 350KB. You know, this is just the size of the transfer. JavaScript tends to be compressed for transmission over the network. That is, the actual size will be much larger after the browser unzips it. And that’s important. If you consider the resource consumption of the browser to process data, compression is a must. A 300KB file can be extracted to 900KB and still be 900KB in size when analyzed and compiled.
In fact, working with JavaScript is very resource-intensive. Unlike images, which have a bit of simple decoding at download time, JavaScript needs to be analyzed, compiled, and then executed. Byte by byte, so JavaScript processing is expensive.
Various improvements have been proposed to optimize the JavaScript engine. Improving the performance of JavaScript code is what developers do best. After all, who better to optimize the performance of an architecture than an architect?
Code Splitting is one of the ways you can improve performance by splitting your JavaScript application into chunks and downloading it only when you need it. This approach is good, but one of the most common problems we don’t address is that there is a lot of packaged code that we don’t use at all. To solve this problem, we use Tree Shaking.
What is tree shaking?
Tree shaking is a way to eliminate dead code. The term was first popularized by the Rollup community, but the idea itself is very old. The same idea exists in Webpack, which we’ll illustrate with an example in this article.
The word “tree shaking” comes from the architecture of an application and its dependencies like a tree structure. Each node of the tree represents a unique function in the application. In modern web applications, dependencies typically use static import statements, as shown below:
// Import all the array utilities!
import arrayUtils from "array-utils";
Copy the code
Note: If you are not familiar with ES6, I highly recommend reading this article on Pony Foo. This article assumes some familiarity with ES6. If not, learn it.
When your app is small, it may have very few dependency files. And you should use almost all of the dependencies you add yourself. However, as your app develops over time, more and more dependencies are added. For various reasons, old dependencies may not be used at all, but they are still in your code base and not removed. You end up with a lot of JavaScript embedded in your app that you’re not using. Tree Shaking removes useless code by analyzing how we use import statements.
// Import only some of the utilities!
import { unique, implode, explode } from "array-utils";
Copy the code
The difference between this import statement and the previous one is that instead of introducing the whole array-utils, which can have a lot of functions, we introduce only what we need. There is no difference between these two uses when developing builds. But at production packaging time, we can configure WebPack to weed out unwanted functions, making the entire code file smaller. In this article, we’ll guide you through how to do it.
case
For demonstration purposes, I’ve written a simple one-page application. You can clone the code and follow suit. I will describe each step in detail, so cloning is not a necessary step.
The example is a database that can search for guitar effects.
When the application is built, all the JavaScript files are packaged into one vendor and one app file.
The file in the image above is the result of packaging and has been uglificated. The 21.1KB size is perfectly acceptable. Currently, however, tree Shaking is not used for optimization. Let’s see how we can optimize it further.
In any application, looking for opportunities to use Tree Shaking optimization starts with looking for import statements. Usually at the top of the Component file, like this:
import * as utils from ".. /.. /utils/utils";
Copy the code
You may have seen this statement. There are several ways to import modules in ES6, but import statements like this are the most notable. Because it means importing all functions in the utils module and putting them under the utils namespace. So, one of the big questions is: how many functions are there in a module?
If you look at the source code for the utils module, you’ll see a lot of it. It’s about 1300 lines of code.
But don’t worry. Maybe all the functions are used in the current file, right? Do we really need all the functions? Let’s check by looking at utils. To see how many are used. As a result:
All right, so we only found three. So let’s see which function is that? If we look at them one by one, we’ll see that only one function is used, utils.simplesort.
if (this.state.sortBy === "model") {
// Simple sort gets used here...
json = utils.simpleSort(json, "model".this.state.sortOrder);
} else if (this.state.sortBy === "type") {
/ /.. and here...
json = utils.simpleSort(json, "type".this.state.sortOrder);
} else {
/ /.. and here.
json = utils.simpleSort(json, "manufacturer".this.state.sortOrder);
}
Copy the code
That is, we imported a 1300-line file and only used one of the functions.
Of course, we have to admit that this example may be deliberate for demonstration purposes. However, it expresses the fact that in many real-world applications, there are optimizations like this. So how do you do that?
Disallow Babel from compiling ES6 to CommonJS
Babel has become essential for many applications. Unfortunately, it makes tree shaking difficult. If you use babel-preset-env, it will compile your ES6 to the more compatible CommonJS.
The problem is that tree shaking is very difficult for CommonJS, and WebPack doesn’t know what needs to be removed. Fortunately, there is an easy solution: configure babel-preset-env to keep ES6 stationary and not to be preset. The specific configuration is placed where you configure Babel (.babelrc or package.json) :
{
"presets": [["env", {
"modules": false}}]]Copy the code
Simply configure “modules”:false, and WebPack will analyze module dependencies in all files and weed out unused code. Also, there will be no compatibility issues with this process, because WebPack will eventually convert the code to a compatible version.
Keep Side Effects in mind
Another consideration is whether there are side effects to using modules in your application. Let me give you an example of what a side effect is (this example describes modifying variables outside a function in a function):
let fruits = ["apple"."orange"."pear"];
console.log(fruits); // (3) ["apple", "orange", "pear"]
const addFruit = function(fruit) {
fruits.push(fruit);
};
addFruit("kiwi");
console.log(fruits); // (4) ["apple", "orange", "pear", "kiwi"]
Copy the code
In this example, addFruit modifies the Fruit array, which is global.
It is safe to shaking only when a function is given an input and produces an output that does not modify anything external.
So, in webpack, we can configure “sideEffects”:false to indicate that the module is safe and has no sideEffects.
{
"name": "webpack-tree-shaking-example"."version": "1.0.0"."sideEffects": false
}
Copy the code
Alternatively, you can tell WebPack which files have side effects:
{
"name": "webpack-tree-shaking-example"."version": "1.0.0"."sideEffects": [
"./src/utils/utils.js"]}Copy the code
In the above configuration, WebPack assumes that all other files are side-effect-free. If you don’t want to add it to package.json, you can configure module.rules.
According to the need to import
We can import only the functions we need to use. In our example, we just need simpleSort:
import { simpleSort } from ".. /.. /utils/utils";
Copy the code
Using the syntax above, we would simply export the simpleSort function. We would simply change utils. SimpleSort to simpleSort:
if (this.state.sortBy === "model") {
json = simpleSort(json, "model".this.state.sortOrder);
} else if (this.state.sortBy === "type") {
json = simpleSort(json, "type".this.state.sortOrder);
} else {
json = simpleSort(json, "manufacturer".this.state.sortOrder);
}
Copy the code
Let’s take a look at the implementation, first reviewing the packaging effect:
Other situations
In most cases, the above method will suffice. But there are always exceptions that leave you scratching your head. Lodash, for example, does not. Since Lodash was not supported by the architecture at the time, some additional work was required: a) installing LoDash-es to replace Lodash; B) Use a slightly different syntax (called cherry-picking):
// This still pulls in all of lodash even if everything is configured right.
import { sortBy } from "lodash";
// This will only pull in the sortBy routine.
import sortBy from "lodash-es/sortBy";
Copy the code
If you prefer to use consistent import syntax, you can use the standard Lodash package and install babel-plugin-Lodash.
Webpack cannot use Tree shaking if some modules use CommonJS format (module.exports). Some plug-ins (Webpack-common-shake) provide tree shaking for CommonJS. However, there are some CommonJS patterns that you can’t do tree shaking. If you want to safely eliminate unused dependencies, ES6 is your best bet.
About Fundebug
Fundebug focuses on JavaScript, wechat applets, wechat mini games, Alipay applets, React Native, Node.js and Java real-time BUG monitoring. Since its launch on November 11, 2016, Fundebug has handled more than 600 million error events in total, which has been recognized by many well-known users such as Google, 360 and Kingsoft software. Welcome free trial!