Webpack 与 Rollup 二者之间该如何选择|原理篇
30 秒速记
- 核心判断:构建工具从入口解析依赖图,经转换和优化生成可在目标运行时加载的模块与资源
- 原理主线:围绕 「快速上手」、「Code Splitting」、「写在最后」 建立输入、状态变化与输出之间的因果关系
- 文章范围:对比分析了 Webpack 和 Rollup 两大前端打包工具的原理、功能和适用场景,帮助开发者根据项目需求选择合适的打包方案,并通过示例演示 Rollup 的基本用法。
- 边界与代价:静态分析受模块语法、动态依赖和副作用声明限制;拆包过细也会增加请求和运行时成本
- 工程落地:优化前先测构建阶段、产物组成和缓存命中,再针对解析、转换、压缩或运行时逐层处理
应用开发通常选 Webpack,类库或框架开发更适合选 Rollup。 Webpack 的工程化能力更完整,适合需要第三方模块、代码拆分和 HMR 的复杂应用;Rollup 更专注于 ES Modules,产物扁平、可读,并默认支持 Tree-shaking。我一般会让组件库用 Rollup 输出多种模块格式,让业务应用交给 Webpack。这只是经验原则,两者都能跨场景使用,最终还要看插件生态和构建需求。
这篇文章不要按 API 清单来背。先用上面的 Mind Map 建立全局结构,再通过交互 DEMO 观察正常路径和边界路径如何改变状态;阅读正文时重点核对每一步的输入、负责执行的参与者、产生的中间状态以及最终可观察结果。遇到版本敏感结论,要把“历史实现”“当前行为”和“工程兼容策略”分开说明;遇到性能或架构取舍,则用实际指标、失败现象和验证手段支撑判断。
版本校准: 旧文中的 webpack 4、JSONP 更新清单或 react-hot-loader 代码用于解释历史链路,不应直接复制到新项目。当前 webpack-dev-server 4+ 默认启用 HMR,严格 ESM 可使用 import.meta.webpackHot;生产环境不得携带 HMR runtime。以 webpack HMR 官方指南 和项目锁定版本为准。
Rollup 是一款 ES Modules 打包器。它也可以将项目中散落的细小模块打包为整块代码,从而使得这些划分的模块可以更好地运行在浏览器环境或者 Node.js 环境。
从作用上来看,Rollup 与 Webpack 非常类似。不过相比于 Webpack,Rollup 要小巧的多。因为 Webpack 在配合一些插件的使用下,几乎可以完成开发过程中绝大多数前端工程化的工作。而 Rollup 可以说仅仅是一个 ES Modules 打包器,没有更多其他的功能了。
例如,在 Webpack 中支持 HMR 这种对开发过程十分友好的功能,而在 Rollup 中就没有办法完全支持。
Rollup 诞生的目的并不是要与 Webpack 这样的工具全面竞争。它的初衷只是希望能够提供一个高效的 ES Modules 打包器,充分利用 ES Modules 的各项特性,构建出结构扁平,性能出众的类库。
至于它的其他特点和优势,我们需要上手过后才能深入了解。
# 快速上手
这里我准备了一个简单的示例,具体结构如下:
.
├── src
│ ├── index.js
│ ├── logger.js
│ └── messages.js
└── package.json
在这个示例的源代码中我准备了三个文件,并且使用 ES Modules 组织的代码模块化。部分代码如下:
// ./src/messages.js
export default {
hi: 'Hey Guys, I am zce~'
}
// ./src/logger.js
export const log = msg => {
console.log('---------- INFO ----------')
console.log(msg)
console.log('--------------------------')
}
export const error = msg => {
console.error('---------- ERROR ----------')
console.error(msg)
console.error('---------------------------')
}
// ./src/index.js
import { log } from './logger'
import messages from './messages'
log(messages.hi)
如上述代码所示,其中:
- messages.js: 文件中以默认导出的方式导出了一个对象;
- logger.js: 文件中单个导出了两个函数成员;
- 最后在 index.js: 文件中导入了这两个模块,并且使用了它们。
接下来,我们尝试使用 Rollup 完成这个示例应用的打包。这里需要先通过 npm 安装 rollup 这个模块。具体命令如下:
$ npm i rollup --save-dev
安装完成过后,rollup 这个模块同样会在 node_modules/.bin 目录中为我们提供一个 CLI 程序,我们就可以通过这个 CLI 去使用 Rollup 打包。具体命令如下:
$ npx rollup
P.S. 对于
node_modules/.bin目录下的 CLI,我们可以使用 npx 命令或者 yarn 命令直接启动。
执行 rollup 命令,在不传递任何参数的情况下,这个命令会自动打印出它的帮助信息。具体如下图:

在这个帮助信息的一开始,就已经告诉我们 rollup 命令的正确用法了:我们应该通过参数指定一个打包入口文件。正确命令如下:
$ npx rollup ./src/index.js
这里指定的打包入口是 src/index.js 文件。再次执行 rollup 命令,具体执行结果如下:

根据控制台的输出结果,我们发现 Rollup 直接将打包结果打印到控制台中了。
当然,正常情况下我们还是需要将打包结果输出到一个文件中。具体就是通过 CLI 的 --file 参数指定输出文件路径,具体命令如下:
$ npx rollup ./src/index.js --file ./dist/bundle.js
这样打包的结果就会输出到文件中。
完成以后,我们找到 Rollup 打包输出的文件,具体结果如下:

在这个文件中我们的第一印象就是,Rollup 打包结果惊人的简洁,基本上就跟我们手写的代码一样。相比于 Webpack 大量的引导代码和一堆的模块函数,这里的输出结果没有任何多余代码,就是把打包过程中的各个模块按照依赖顺序,先后拼接到了一起。
而且我们仔细观察打包结果,你会发现,在我们输出的结果中只会保留那些用到的部分,对于未引用部分都没有输出。这是因为 Rollup 默认会自动开启 Tree-shaking 优化输出结果,Tree-shaking 的概念最早也就是 Rollup 这个工具提出的。
# 配置文件
Rollup 同样支持以配置文件的方式去配置打包过程中的各项参数,我们可以在项目的根目录下新建一个 rollup.config.js 的配置文件。具体结构如下:
.
├── src
│ ├── index.js
│ ├── logger.js
│ └── messages.js
├── package.json
+└── rollup.config.js
这个文件虽然同样是运行在 Node.js 环境中,但是 Rollup 会额外处理配置文件,所以在 rollup.config.js 中我们可以直接使用 ES Modules 标准。具体代码如下:
// ./rollup.config.js
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'es' // 输出格式
}
}
这个文件中需要导出一个配置对象,在这个对象中我们可以通过 input 属性指定打包的入口文件路径,通过 output 指定输出相关配置,output 属性是一个对象,在 output 对象中可以使用 file 属性指定输出的文件名,format 属性指定输出代码的格式。
完成以后,我们回到命令行,再次执行 rollup 命令,不过需要注意的是,这里需要通过 --config 参数来表明使用项目中的配置文件。你也可以通过这个参数来指定不同的配置文件名称。具体命令如下:
$ npx rollup --config # 使用默认配置文件
$ npx rollup --config rollup.prod.js # 指定配置文件路径
# 输出格式
Rollup 打包支持多种输出格式,这里我们回到配置文件中,配置同时输出所有格式下的文件,具体配置如下:
// ./rollup.config.js
// 所有 Rollup 支持的格式
const formats = ['es', 'amd', 'cjs', 'iife', 'umd', 'system']
export default formats.map(format => ({
input: 'src/index.js',
output: {
file: `dist/bundle.${format}.js`,
format
}
}))
在这个配置当中我们导出了一个数组,数组中的每个成员都是一个单独的打包配置,这样 Rollup 就会分别按照每个配置单独打包。这一点与 Webpack 非常相似。
配置完成之后,我们回到命令行终端,再次运行 Rollup 打包。那这次打包过后,dist 目录下就会生成不同格式的输出结果,如下图所示:

你可以自己依次去了解一下每种格式的输出结果,其实不同的输出格式大都是为了适配不同的运行环境,并没有什么需要额外理解的地方。
# 使用插件
Rollup 自身的功能就只是 ES Modules 模块的合并,如果有更高级的要求,例如加载其他类型的资源文件或者支持导入 CommonJS 模块,又或是编译 ES 新特性,这些额外的需求 Rollup 同样支持使用插件去扩展实现。
Webpack 中划分了 Loader、Plugin 和 Minimizer 三种扩展方式,而插件是 Rollup 的唯一的扩展方式。
这里我们先来尝试使用一个可以让我们在代码中导入 JSON 文件的插件:@rollup/plugin-json,通过这个过程来了解如何在 Rollup 中使用插件。
首先我们需要将 @rollup/plugin-json 作为项目的开发依赖安装进来。具体安装命令:
$ npm i @rollup/plugin-json --save-dev
安装完成过后,我们打开配置文件。由于 rollup 的配置文件中可以直接使用 ES Modules,所以我们这里使用 import 导入这个插件模块。具体代码如下:
// ./rollup.config.js
import json from '@rollup/plugin-json'
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'es'
},
plugins: [json()]
}
