Rollup 库打包:Tree-shaking 友好的模块打包器

FreeGuideOnline 最新 2026-06-15

什么是 Rollup

Rollup 是一款专门为 JavaScript 库和组件设计的模块打包器。与 Webpack 等通用打包工具不同,Rollup 的核心优势在于 Tree-shaking(摇树优化)——它能够分析模块依赖图,并自动移除未使用到的代码(dead code),最终生成极致精简的 bundle。这使得 Rollup 特别适合发布可被其他项目引用的库,因为它输出的代码干净、体积小,且对 ES 模块(ESM)生态极其友好。

为什么用 Rollup 打包库

  • 原生 Tree-shaking 支持:基于静态的 ES 模块导入/导出语法,精确消除未引用代码。
  • 输出格式灵活:支持多种输出格式(IIFE、CJS、AMD、UMD、ESM),同一份配置可生成多个版本。
  • 生成可读性高的 bundle:打包结果接近手写代码,便于调试,不会引入大量运行时代码。
  • 专为库优化:更适合构建可复用的模块,而不是包含大量资源的应用。

安装 Rollup

在项目中安装 Rollup 作为开发依赖:

npm install --save-dev rollup

或使用 yarn:

yarn add --dev rollup

安装后你就可以在命令行直接使用 npx rollup

第一个 bundle

创建一个简单的库入口文件 src/main.js

// src/main.js
import { add } from './math.js';

export function sumThree(a, b, c) {
  return add(add(a, b), c);
}

export const version = '1.0.0';

提供依赖模块 src/math.js

// src/math.js
export function add(x, y) {
  return x + y;
}

export function subtract(x, y) {
  return x - y;
}

在终端执行打包命令:

npx rollup src/main.js --file dist/bundle.js --format esm

打开 dist/bundle.js,你会看到 Rollup 已经将代码合并为一个文件,并且 subtract 函数被完全移除了,因为它从未被 main.js 使用。这就是最基础的 Tree-shaking 效果。

配置文件

在实际项目中,我们需要更复杂的构建配置,通过 rollup.config.mjs 文件管理。

// rollup.config.mjs
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';

export default {
  input: 'src/main.js',               // 入口文件
  output: {
    file: 'dist/bundle.js',           // 输出文件
    format: 'esm',                    // 输出格式:ES Module
    sourcemap: true,                  // 生成 source map
  },
  plugins: [
    resolve(),     // 解析第三方模块(node_modules)
    commonjs(),    // 将 CommonJS 模块转换为 ESM,以便 Tree-shaking
    terser(),      // 压缩代码(可选)
  ],
};

运行打包时指定配置文件:

npx rollup -c rollup.config.mjs

关键插件推荐

插件 功能 安装命令
@rollup/plugin-node-resolve 查找 node_modules 中的第三方模块 npm i -D @rollup/plugin-node-resolve
@rollup/plugin-commonjs 将 CommonJS 模块转为 ESM,使其可被 Tree-shaking npm i -D @rollup/plugin-commonjs
@rollup/plugin-babel 使用 Babel 转译代码(如语法的降级) npm i -D @rollup/plugin-babel @babel/core @babel/preset-env
rollup-plugin-terser 压缩输出代码 npm i -D rollup-plugin-terser

在配置文件的 plugins 数组中按顺序添加这些插件即可生效。

输出格式详解

Rollup 通过 output.format 控制生成的模块格式。对于库开发者,通常需要同时输出多种格式以满足不同消费场景。

ES Module(esm)

现代打包器和浏览器原生支持的模块格式。
优点:支持静态分析,Tree-shaking 发挥最佳作用。
用法:在 package.jsonmodule 字段指向该文件,让使用者打包时能进一步消除无用代码。

output: {
  file: 'dist/mylib.esm.js',
  format: 'esm',
}

CommonJS(cjs)

Node.js 传统模块格式。
主要用于 require() 引入的场景。
package.jsonmain 字段指向该文件。

output: {
  file: 'dist/mylib.cjs.js',
  format: 'cjs',
  exports: 'named',  // 可选:支持具名导出
}

Universal Module Definition(umd)

兼容浏览器全局变量、AMD 和 CommonJS 的格式。
适合直接通过 <script> 标签加载,或在较旧的环境中运行。

output: {
  file: 'dist/mylib.umd.js',
  format: 'umd',
  name: 'MyLib',   // 全局变量名
}

推荐做法:一次构建输出所有三种格式,可使用 output 为数组:

output: [
  { file: 'dist/mylib.esm.js', format: 'esm' },
  { file: 'dist/mylib.cjs.js', format: 'cjs', exports: 'named' },
  { file: 'dist/mylib.umd.js', format: 'umd', name: 'MyLib' },
],

同时记得更新 package.json

{
  "main": "dist/mylib.cjs.js",
  "module": "dist/mylib.esm.js",
  "browser": "dist/mylib.umd.js"
}

Tree-shaking 如何工作

Rollup 依赖 ES 模块的静态结构 实现 Tree-shaking。ES 模块的 importexport 语句在编译阶段即可确定依赖关系,而 CommonJS 的 require() 是动态的,无法在构建时分析。

过程简述

  1. 从入口模块开始,通过 import 语句构建模块依赖图。
  2. 遍历依赖图,标记所有被实际引用的 export(包括间接引用)。
  3. 移除未被任何引用触及的 export,连同其代码一并删除。
  4. 将剩余代码合并为单个文件(或按指定分块),同时尽可能保留模块边界(bundling 但不破坏结构)。

注意事项

  • 避免 export default {} 导出整个对象,这样会引入所有属性,破坏 Tree-shaking。应使用具名导出。
  • 副作用处理:若模块内包含顶层副作用代码(如修改 window),即便未引用其导出,Rollup 也可能保留。可通过 package.jsonsideEffects 字段告知哪些文件无副作用。
  • 使用 CommonJS 依赖时,必须搭配 @rollup/plugin-commonjs 将语法转换为 ES 模块,否则无法参与 Tree-shaking。

处理第三方依赖和代码分块

库开发时,通常无需将 React、Vue 等大型依赖打入 bundle 中,而是将其设为外部(external),由使用者自行提供。

export default {
  input: 'src/index.js',
  external: ['react', 'react-dom'],
  output: {
    globals: {
      react: 'React',
      'react-dom': 'ReactDOM',
    },
    // 仅 iife/umd 格式需要 globals,其他格式自动处理
  },
};

如果需要代码拆分(code splitting),Rollup 也支持动态导入,只需将 output.format 设为 esmsystem,并使用 output.dir 代替 output.file,Rollup 会自动生成多个 chunk。

常见问题

为什么 Rollup 不打包图片/CSS?

Rollup 的核心理念是纯粹的 JavaScript 打包。图片、字体等资源需借助插件(如 rollup-plugin-styles@rollup/plugin-image)才能处理,但库发布时一般不建议内联这些资源,通常留给应用层打包工具处理。

如何处理全局变量注入?

使用 @rollup/plugin-replace,可以像定义环境变量一样注入常量,例如 process.env.NODE_ENV

TypeScript 支持?

安装 @rollup/plugin-typescripttslib,在配置中加入即可直接打包 .ts 文件,同时也能享受 Tree-shaking。

为什么 bundle 中仍有未使用的 import?

检查是否存在副作用导入(如 import './polyfills'),或某模块包含顶级副作用。在 package.json 声明 "sideEffects": false 有助于 Rollup 更激进地移除代码,但必须确保整个项目无副作用,或精确列出有副作用的文件列表。

总结

Rollup 凭借其优异的 Tree-shaking 能力和清晰的输出,已成为 JavaScript 库打包的事实标准。通过掌握配置、选择合适的输出格式、结合关键插件,你可以构建出体积小巧、易于集成、且能被进一步优化的库。无论是开源项目还是公司内部组件库,Rollup 都是值得信赖的模块打包器。