Frontend Development 14 min read

Understanding Tree‑shaking in Rollup and Webpack: Mechanisms, Configurations, and Comparison

This article explains the purpose and ES6‑module basis of tree‑shaking, compares the implementation details of Rollup and Webpack, provides configuration and code examples, and shows real‑world bundle size and build‑time results to help developers choose the right bundler for optimal output.

政采云技术
政采云技术
政采云技术
Understanding Tree‑shaking in Rollup and Webpack: Mechanisms, Configurations, and Comparison

Rollup and Webpack are two of the most widely used bundlers in modern front‑end projects; both rely on tree‑shaking to eliminate dead code and reduce the final bundle size, which is increasingly important for performance‑critical applications.

Purpose of Tree‑shaking

The sole goal of tree‑shaking is to remove three categories of dead code: unreachable code, code whose execution result is never used, and code that only writes to variables without reading them, thereby enabling true on‑demand imports.

Why Tree‑shaking Depends on ES6 Modules

ES6 modules are static: they can only appear at the top level, import specifiers must be string literals, and imports are immutable. This static nature allows reliable analysis, unlike CommonJS where require is dynamic.

// CommonJS example
if (hasRequest) {
  const utils = require('utils');
}

With ES6 you can import only the needed symbols, e.g. import { request } from 'utils'; , and the import statement itself cannot be placed inside conditional statements.

// ES6 import example
import { request } from 'utils';

Webpack 5.x Tree‑shaking Mechanism

Webpack 2 introduced built‑in support for ES2015 (harmony) modules and side‑effects detection via the sideEffects field in package.json . Webpack 5 adds the terser-webpack-plugin for code compression. During compilation Webpack marks imports/exports and lets the optimizer drop unused parts.

1
      ? `/* unused harmony exports ${joinIterableWithComma(this.unusedExports)} */\n`
      : this.unusedExports.size > 0
      ? `/* unused harmony export ${first(this.unusedExports)} */\n`
      : "";
    const definitions = [];
    const orderedExportMap = Array.from(this.exportMap).sort(([a], [b]) => a < b ? -1 : 1);
    // mark harmony export
    for (const [key, value] of orderedExportMap) {
      definitions.push(`\n/* harmony export */   ${JSON.stringify(key)}: ${runtimeTemplate.returningFunction(value)}`);
    }
    const definePart = this.exportMap.size > 0
      ? `/* harmony export */ ${RuntimeGlobals.definePropertyGetters}( ${this.exportsArgument}, {${definitions.join(",")}
/* harmony export */ });\n`
      : "";
    return `${definePart}${unusedPart}`;
  }

Rollup Tree‑shaking Mechanism

Rollup uses a pipeline of static analysis: it builds a module graph from the entry file, generates an AST with Acorn, marks each node as used or unused, and finally emits code with MagicString, stripping dead code.

{
  "name": "rollup",
  "version": "2.77.2",
  "description": "Next-generation ES module bundler",
  "main": "dist/rollup.js",
  "module": "dist/es/rollup.js",
  "typings": "dist/rollup.d.ts",
  "bin": { "rollup": "dist/bin/rollup" },
  "devDependencies": {
    "@rollup/plugin-alias": "^3.1.9",
    "@rollup/plugin-commonjs": "^22.0.1",
    "acorn": "^8.7.1",
    "magic-string": "^0.26.2"
    // ...
  }
}

Rollup Advantages

Exports pure ES modules.

Performs program‑flow analysis to more accurately detect side‑effects.

Case Studies

Import without usage cannot be eliminated import pkgjson from '../package.json'; export function getMeta(version) { return { lver: version || pkgjson.version }; } The whole package.json object is bundled.

Global variable mutation prevents removal window.utm = 'a.b.c'; Even if utm is never read, the statement stays because it may affect the global scope.

Vue 3 Tree‑shaking Optimizations

Vue 2’s global API Vue.nextTick cannot be tree‑shaken because it is not exported individually. Vue 3 refactors such APIs to enable proper dead‑code elimination.

Final Comparison

Configuration snippets for both bundlers are shown below.

Webpack config (simplified):

const webpack = require('webpack');
const path = require('path');
module.exports = {
  entry: path.join(__dirname, 'src/index.ts'),
  output: { filename: 'webpack.bundle.js' },
  module: { rules: [ { test: /\.(js|ts|tsx)$/, exclude: /(node_modules|bower_components|lib)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.tsx?$/, use: 'ts-loader', exclude: /(node_modules|lib)/ } ] },
  resolve: { extensions: ['.tsx', '.ts', '.js'] },
  optimization: { usedExports: true },
  plugins: [ new webpack.optimize.ModuleConcatenationPlugin() ]
};

Rollup config (simplified):

import resolve from "rollup-plugin-node-resolve";
import commonjs from "rollup-plugin-commonjs";
import typescript from "rollup-plugin-typescript2";
import babel from "rollup-plugin-babel";
import json from "rollup-plugin-json";
import { uglify } from 'rollup-plugin-uglify';
export default {
  input: "src/index.ts",
  output: [{ file: "lib/index.cjs.js", format: "cjs" }],
  treeshake: true,
  plugins: [ json(), typescript(), resolve(), commonjs(), babel({ exclude: "node_modules/**", runtimeHelpers: true, sourceMap: true, extensions: [".js", ".jsx", ".es6", ".es", ".mjs", ".ts", ".json"] }), uglify() ]
};

Measured bundle sizes and build times:

Tool

Before (KB)

After (KB)

Build Time

Webpack 5.52.0

46 KB

44 KB

4.8 s

Rollup 1.32.1

24 KB

18 KB

3.7 s

Conclusion

Rollup generally produces smaller bundles and faster builds for this SDK example, but the actual impact depends on project size, side‑effects configuration, and usage patterns. Understanding how each bundler marks and removes unused code helps developers write more tree‑shakable modules.

References

Vite Features and Partial Source Analysis

Rollup Source Code Analysis

Rollup Tree‑shaking Documentation

frontendwebpacktree-shakingRollupES6 ModulesBundling
政采云技术
Written by

政采云技术

ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.