第4章 用在哪里
14 分钟阅读
第 4 章 用在哪里
4.1 开源库 / npm 包发布
如果说 Rollup 是前端构建工具链中的瑞士军刀,那 npm 包发布就是它最闪耀的舞台。为啥?因为 Rollup 天生就是为了"精准投放"而生的——它只想把你写的代码打包成最小可用的形式,不会往包里塞一堆乱七八糟的东西。
4.1.1 Vue 生态(Vue 3 自身源码使用 Rollup 打包)
Vue 3 是最早大规模采用 Rollup 的知名项目之一。Vue 3 的 npm 发布包(vue、@vue/runtime-core、@vue/runtime-dom、@vue/reactivity 等)就是用 Rollup 打包的。当你 npm install vue 的时候,安装的包就是由 Rollup 打包生成的产物——这直接证明了 Rollup 在大型项目中的可靠性。
Vue 官方团队选择 Rollup 的理由很简单:Tree-Shaking 做得好,产物小,Vue 用户只需要为他们真正用到的功能付费。比如你没有用 v-model,就不会打包相关代码;没有用 Transition 组件,对应的运行时就不会打包进去。
4.1.2 React 生态(许多 React 库采用 Rollup 构建)
React 生态中有很多知名库使用 Rollup 打包:
- react-router:React 生态最流行的路由库
- formik:表单处理库
- react-final-form:高级表单状态管理库
- date-fns:一个"可 tree-shake" 的日期处理库(相比 moment.js 体积小得多)
这些库选择 Rollup 的共同原因是:让消费者只加载他们用到的部分。date-fns 如果不打包成支持 Tree-Shaking 的形式,用户引入整个库会是 ~100KB;但通过 Rollup 打包后,用户只引入 format 和 parse 两个函数,最终体积可能只有 ~10KB。
4.1.3 工具库(rxjs / express 等)
RxJS 是响应式编程领域最流行的库,它的体积极其庞大(完整引入 ~2MB)。通过 Rollup 打包成支持 Tree-Shaking 的 ESM 格式后,用户可以只引入自己用到的操作符:
| |
💡 RxJS 7+ 小贴士:RxJS 7+ 统一了导入方式,大多数操作符都可以直接从
rxjs导入(比如import { map, filter } from 'rxjs'),不再强制要求从rxjs/operators导入。如果你看到老代码里import { map } from 'rxjs/operators',那是 RxJS 6 的写法——现在属于"活化石"级别的东西了,看到可以顺手升级一下。
Express 是 Node.js 生态最流行的 Web 框架之一,它中间件的注册机制让用户可以按需引入需要的部分——这和 Rollup 打包的思路有异曲同工之处。
4.1.4 UI 组件库(Ant Design / Element UI 构建工具链)
Ant Design 是 React 生态中最知名的 UI 组件库之一,它的构建工具链颇为复杂——Gulp 负责整体流程编排,Rollup 负责组件级别的打包和 Tree-Shaking,最终产物同时支持 ESM/CJS/UMD 多种格式。
Element Plus(Vue 3)也是类似,用 Rollup 打包出体积最小化的组件库,让用户按需引入。需要注意的是,老版的 Element UI(Vue 2)构建配置更为繁复(gulp + rollup + webpack 的混合配置),而新版的 Element Plus 则更加统一和简洁。
4.2 前端工具链开发
前端工具链的开发者是 Rollup 的重度用户群体——毕竟做工具的人自己也得用工具,而工具最大的追求就是:快、小、稳。Rollup 完美符合这三个要求,所以 Babel、ESLint、Vite 这些大名鼎鼎的工具都选择用它来打包自己的插件和核心代码。
4.2.1 Babel 插件
Babel 插件本质上是一些接收 Babel API 参数、返回转换器的函数。Babel 官方团队使用 Rollup 来打包所有 Babel 插件。
Babel 插件的结构通常是这样的:
| |
这个插件演示了 Babel 插件的核心工作方式:通过 visitor 对象访问特定类型的 AST 节点,在节点上读取或修改信息。Babel 官方团队选择 Rollup 来打包这些插件,主要是因为 Rollup 输出干净、Tree-Shaking 效果好——毕竟插件本身可能很小巧,用户不希望因为装了一个插件而多打包一堆无关代码。
用 Rollup 打包后,这个插件就可以作为一个 npm 包被其他项目使用了。
4.2.2 Webpack Loader / Plugin
Webpack Loader 的作用是"翻译"特定类型的文件。比如 babel-loader 的工作就是把 ES6+ 代码转成 ES5,sass-loader 的工作是把 SCSS 转成 CSS。
Webpack Plugin 的作用则是在 Webpack 打包过程中的特定时机插入自定义逻辑。
这两者的打包同样适合用 Rollup:
| |
4.2.3 ESLint 插件 / Rules
ESLint 插件就是 ESLint 规则的集合。ESLint 团队官方使用 Rollup 来打包所有官方插件:
| |
4.2.4 Vite 插件
Vite 插件本质上是一个返回特定生命周期钩子对象(或者函数)的工厂。Vite 官方使用的就是 Rollup 作为底层打包引擎,所以 Vite 插件实际上就是在 Rollup 的插件系统上封装了一层:
| |
这个插件展示了 Vite 插件的典型模式:在 transform 钩子里对代码做转换处理。Vite 官方选择 Rollup 作为底层打包引擎,正是看中了 Rollup 插件系统的简洁和高效——Vite 插件本质上就是 Rollup 插件的超集(Vite 在 Rollup 插件接口基础上扩展了自己独有的钩子)。
4.2.5 微前端框架(qiankun 等子应用打包)
微前端是一种将大型前端应用拆分为多个独立子应用的架构模式。每个子应用都可以独立开发、独立部署,Rollup 打包出来的干净小产物在这种场景下简直是量身定做。
qiankun 是蚂蚁集团开源的微前端框架,它支持多种打包方式(Rollup、Vite、Webpack 均可)。使用 Rollup 打包子应用的优势在于:产物干净、体积小、加载快,非常适合微前端场景下对性能的严苛要求。如果子应用使用 Vite 开发,直接用 Vite 打包也是完全可行的——qiankun 官方对这两种方式都有良好支持。
💡 qiankun + Rollup 的最佳实践:子应用的入口文件建议使用
format: 'iife'或format: 'system',因为 qiankun 的沙箱机制需要子应用导出为一个全局函数。Rollup 对这两种格式的支持非常好,配上name字段即可。
4.3 Vite 生产构建
这是 Rollup 被使用得最广泛的场景——尽管很多开发者可能没有意识到他们在"用 Rollup"。想象一下:全球几百万个项目用 Vite 构建生产代码,而这些项目的最终产物,都出自 Rollup 之手。Rollup 在幕后默默干活,却很少被人记起——这大概就是开源世界里的"幕后英雄"吧。
4.3.1 Vite 架构:开发用 esbuild,生产用 Rollup
Vite 是一个"开发时快如闪电,生产时质量过硬"的构建工具。它的架构设计非常聪明:
- 开发阶段:使用 esbuild 做依赖预构建和即时转译。esbuild 是用 Go 语言写的,打包速度极快(比 Webpack 快 10-100 倍),Vite 启动时会先对项目依赖进行预构建(把 CJS 转成 ESM、合并 import 等),之后用户访问页面时请求的代码已经是预转译好的 ESM,用户几乎感觉不到等待。
- 生产阶段:使用 Rollup 做最终打包。Rollup 的 Tree-Shaking 和多格式输出能力能确保最终产物既小又快。
graph LR
A["开发阶段<br/>启动 Vite Dev Server"] --> B["esbuild 依赖预构建<br/>(CJS→ESM、合并 import)"]
B --> C["用户访问页面"]
C --> D["浏览器请求 ES Module<br/>(已预转译)"]
D --> E["热更新 HMR<br/>仅重新加载变化的部分"]
graph LR
F["生产阶段<br/>执行 vite build"] --> G["Rollup 打包"]
G --> H["优化产物<br/>Tree-Shaking + 压缩"]
H --> I["部署到服务器"]esbuild 依赖预构建:这是 Vite 开发阶段的第一步,会把项目依赖(如
node_modules中的 CJS 库)提前转成 ESM,并处理import合并等。这不是"访问时"才发生,而是在你敲下vite命令后就悄悄完成了。
这种"开发用 esbuild,生产用 Rollup"的架构,既保证了开发体验,又保证了产物质量,是 Vite 成为现代前端主流工具的重要原因。
4.3.2 vite build 命令底层调用 Rollup
当你执行 npm run build(对应的命令就是 vite build)时,Vite 实际上就是在调用 Rollup:
| |
这个过程完全由 Rollup 完成,Vite 只是做了一个"包裹"和"配置传递"的工作。
4.3.3 通过 vite.config.js 的 rollupOptions 自定义配置
如果你需要在 Vite 的 Rollup 构建过程中做一些特殊配置,可以通过 vite.config.js 的 rollupOptions 来做:
| |
4.3.4 动态导入与代码分割(Code Splitting)
Rollup 的另一个杀手锏是动态导入(Dynamic Import)。配合 input 配置成对象(或在插件中返回 Promise),可以实现自动代码分割:
| |
在实际项目中,动态导入最典型的用法是配合 ES6 动态 import():
| |
Rollup 会把这个动态 import 单独打包成一个 chunk,用户只有在触发这个 import 时才会请求对应的 JS 文件——这对于首屏加载优化极其重要。
4.4 Monorepo 项目
Monorepo(单体仓库)是一种将多个相关项目放在同一个代码仓库中管理的架构方式。Rollup 在 Monorepo 中有独特的价值。
4.4.1 子模块独立打包
在 Monorepo 中,一个仓库包含多个子包(比如 packages/utils、packages/ui)。每个子包通常需要独立打包发布。
Rollup 可以为每个子包配置独立的打包任务:
| |
4.4.2 共享包的构建与发布
在 Monorepo 中,共享包被多个子包依赖。Rollup 可以高效地处理这种场景——把共享包打包成支持 Tree-Shaking 的格式,让每个子包在打包时只引入自己真正用到的部分。
典型的共享包配置如下:
| |
⚠️ 一个重要的坑:Rollup 的 Tree-Shaking 对 CommonJS 模块支持有限。如果你写的库依赖了 CJS 格式的 npm 包,Rollup 可能无法正确分析其导出结构,导致 Tree-Shaking 失效。最佳实践是:始终把你的库打包成纯 ESM 格式,并确保依赖的 npm 包也优先使用 ESM 版本(很多现代库都提供了
exports字段指向 ESM 构建)。
4.5 Electron / Web Extensions
4.5.1 Electron 主进程与渲染进程打包
Electron 应用同时包含"主进程"(运行在 Node.js 环境)和"渲染进程"(运行在浏览器环境)。这两种环境对代码格式的要求不同:主进程通常使用 CommonJS(因为 Electron 主进程默认是 CJS),而渲染进程因为要跑在浏览器里,需要 ESM 或 IIFE 格式。Rollup 可以分别为两种环境打包出合适的产物:
| |
4.5.2 浏览器扩展(Chrome Extension)打包
Chrome 扩展(以及 Firefox、Edge 等浏览器的扩展)本质上是 HTML + CSS + JavaScript 的组合。Rollup 可以把多个脚本文件打包成一个(或几个),同时支持 manifest.json 配置中指定的 CSP(内容安全策略)。
| |
4.6 Rollup 不适合的场景
说完 Rollup 能做什么,也要诚实地说说它不适合什么。
4.6.1 大型 Web 应用热更新开发(用 Vite / Webpack Dev Server)
Rollup 的设计初衷就不是"开发服务器",它没有真正意义上的 HMR(热模块替换)能力。Rollup 的 watch 模式更像是"检测文件变化 → 重新打包",而不是"只更新变化的模块,页面状态保持不变"。虽然可以通过插件实现简单的热更新,但和 Vite / Webpack 的开发服务器比起来,体验差距不是一星半点——简直是走路和坐火箭的区别。
HMR(Hot Module Replacement,热模块替换)是什么? HMR 是一种开发时的高级功能:当你的代码发生变化时,不需要刷新整个页面,只需要替换(更新)变化的那部分模块,页面的其他部分保持不变。对于有复杂状态的前端应用(比如 Vue/React 应用),HMR 可以让你在修改代码后瞬间看到效果,而不需要重新操作一遍应用状态。想象一下:你花 10 分钟填好了一个表单,因为改了一行 CSS 要重新填——HMR 就是来拯救你的。
Rollup 的 watch 模式更偏向于"文件变了,重新打包",而不是"局部更新"——这在大型应用里意味着你每次保存都要等完整的增量构建,虽然比全量快,但还是会打断思路。
4.6.2 需要强 HMR(热模块替换)实时重载的场景
如果你需要一个在保存代码后毫秒级看到效果的开发体验,Rollup 同样不是你的菜。这个场景应该用 Vite(开发服务器用 esbuild 即时转译)或 Webpack(内置成熟 HMR)——它们的 watch + 热更新链路是专门为"改一行代码等三秒"这种痛苦场景优化的。
4.6.3 watch 模式对大项目的性能瓶颈
Rollup 的 watch 模式在小型项目中表现良好,但当项目规模变大(几百个模块)时,每次文件变化触发的增量构建也会开始变慢。对于这种场景,建议直接使用 Vite 或 Webpack,它们有更完善的增量构建缓存机制。
4.6.4 需要 polyfill 的浏览器兼容场景
Rollup 本身不处理 polyfill。如果你需要兼容旧版浏览器(比如要支持 Promise、Array.prototype.includes 等),Rollup 不会像 Webpack 5 那样自动注入 polyfill。这种场景下,要么在源码中手动引入 polyfill(如 core-js),要么在构建链中额外加一层处理(比如 Rollup 打包后再用 esbuild 或 Babel 处理一遍)。
说了这么多"不适合",你可能会想:Rollup 是不是很弱?恰恰相反——知道自己不擅长什么,是它最强的优点。Rollup 从来不试图做一个全能的构建工具,它只做自己擅长的事,并且做到极致。正是因为这种专注,Rollup 才能在"库打包"和"Vite 底层"这两个领域做到无可替代。
本章小结
这一章我们系统地梳理了 Rollup 的主要应用场景:
开源库 / npm 包发布:Vue 生态、React 生态、工具库、UI 组件库,Rollup 是这些场景的首选打包工具。它让用户"只为你用到的功能付费"。
前端工具链开发:Babel 插件、Webpack Loader/Plugin、ESLint 插件、Vite 插件、微前端子应用,都适合用 Rollup 打包。工具链追求快、小、稳,Rollup 完美契合。
Vite 生产构建:Vite 的生产构建底层就是 Rollup,所有使用 Vite 的开发者都在间接使用 Rollup。动态导入与代码分割(Code Splitting)也是通过 Rollup 的
manualChunks和动态import()实现的。Monorepo 项目:子模块独立打包、共享包的高效构建与发布——Rollup 让 monorepo 中的包既能独立发布,又能被其他包按需引用。
Electron / Web Extensions:主进程、渲染进程、浏览器扩展都可以用 Rollup 打包,按需生成不同格式的产物。
不适用的场景:大型 Web 应用的热更新开发、强 HMR 需求场景、watch 模式对大项目的性能瓶颈——这些场景应该选择 Vite 或 Webpack。
一句话总结:Rollup 是那种"知道自己擅长什么"的工具——它不是万能的,但在它擅长的领域(库打包、工具链、Vite 底层),它几乎是无可替代的存在。用好 Rollup,就是让它做它最擅长的事,而不是强迫它去干热更新开发服务器的活儿。