Tree Shaking is an important optimization in Webpack 4 that automatically removes unused code during bundling. But many people just set mode: 'production' and think they're done — there's actually a lot of underlying principle worth understanding.
What Is Tree Shaking
The name "Tree Shaking" comes from shaking a tree to drop its dead leaves. In Webpack's context, "dead leaves" are module exports that were never imported anywhere.
Its core prerequisite: the static structure of ES Modules.
javascript
// math.js - exports add and subtract
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// app.js - only uses add
import { add } from "./math";
console.log(add(1, 2));
// subtract is never referenced → should be removed
After bundling, subtract should not appear in the final output.
Why ESM Can Do It, But CommonJS Cannot
javascript
// CommonJS - dynamic loading, can't determine exports at compile time
const math = require("./math");
math.add(1, 2);
// Problem: what properties does the math object have?
// Only known at runtime → static analysis impossible
// More extreme case
const modules = require("./modules");
const name = getModuleName();
modules[name](); // completely unanalyzable
javascript
// ESM - static imports, dependency graph is known at compile time
import { add } from "./math";
// 1. Imported identifiers are fixed at compile time (cannot be inside an if)
// 2. Module exports are static (cannot be dynamically modified)
// 3. Module top-level execution, no conditional branches
Webpack 4 Tree Shaking Workflow
Webpack 4 Tree Shaking has two phases: marking and elimination.
javascript
// utils.js
export function used() {
// ← marked as "used"
return "I am used";
}
export function unused() {
// ← marked as "unused"
return "I am unused";
}
javascript
// webpack.config.js
module.exports = {
mode: "production", // enables Tree Shaking and Terser
optimization: {
usedExports: true, // mark unused exports
minimize: true, // Terser removes the dead code
},
};
Common Reasons Tree Shaking Doesn't Work
- Using CommonJS:
require()prevents static analysis - Side effects: mark pure modules in
package.json:json{ "sideEffects": false } // or list specific files with side effects: { "sideEffects": ["./src/polyfills.js", "*.css"] } - Babel transforms ESM to CommonJS: make sure Babel doesn't transform
import/exportjson{ "presets": [["@babel/preset-env", { "modules": false }]] }
Tree Shaking is not magic — it requires ES Modules, production mode, and side-effect-free code to work correctly.