做Webpack工程优化这段时间,我最大的感受是:调参只能解决局部痛点,结构才能解决全局问题。很多人一听到模块打包优化,回手就是splitChunks、thread-loader、持久化缓存,这些确实管用,但当一组稳定核心逻辑被业务入口反复引用,又因为依赖关系被Webpack重复编译几百次时,问题往往不在配置参数,而在打包职权没有分开。这时候在Webpack旁边加一个Rollup子方案,专门处理那种高复用、纯JS、低副作用的模块池,反而是性价比非常高的做法。
这篇文章不会劝你把整个工程从Webpack换到Rollup,而是分享一种混合构建的思路:保留Webpack作为主构建器,同时把符合条件的核心模块抽成独立子工程,借助Rollup完成预打包和Tree Shaking,产物直接交给Webpack消费。这套模式能缓解构建时间膨胀、产物体积虚高、运行时模块包装冗余三个问题,也适配“Webpack项目里局部调用wasm模块”这类特殊场景。适合已经熟悉Webpack配置、正在做前端工程性能提升的团队,或者需要在多个入口、多个子应用之间复用公共核心库的工程。
1. 先搞清思路:Webpack负责调度,Rollup负责精简核心模块
1.1 Webpack不是不行,是大而全的代价越来越明显
Webpack本质上是一个通用模块打包器,它的设计目标是处理前端工程里的所有资源:JS、CSS、图片、字体、wasm,还要兼顾HMR、代码分割、多页应用、loader机制和插件生态。这种全能型设计带来的问题是,为了兼容各种模块形态,Webpack在构建产物里引入了自己的模块运行时,每个模块都会被一层函数包裹,模块之间靠__webpack_require__来串联。
对于业务代码来说,这层运行时完全可以接受,反正不管怎样你都得靠模块系统加载依赖。但对于一块长期稳定、被多个入口复用的核心逻辑模块,这层包装就有不少冗余了。你可以把它理解成一个旅行团,Webpack就像一个什么都帮你背的管家,什么资源都能塞进箱子,确实省心,但箱子永远比实际需要的大一号。Rollup恰好相反,它只对ES Module负责,基于import/export的静态结构做分析,打出来的包非常干净,接近手写代码,Tree Shaking也做得更彻底。
Webpack 5的Tree Shaking和experiments.outputModule确实进步明显,但它的底层实现要照顾太多模块类型和插件介入点,面对一个纯函数库的“精确裁剪”需求时,仍不如Rollup极致。所以我的结论不是“Webpack不行”,而是不该让Webpack承担所有打包工作,尤其当某块代码本身不需要Webpack那套复杂生态的时候。
1.2 满足这几个条件的模块,才值得交给Rollup预打包
Rollup子方案不是把任何代码都塞过去就完事。根据我踩过的坑,你至少要让准备拆分的模块满足以下条件,否则后期维护会非常痛苦:
- 代码形态是纯JS或纯ESM,不依赖CSS、图片、字体等静态资源。
- 逻辑本身没有DOM操作,不跟浏览器环境强绑定,至少是“框架无关”的。
- 模块的依赖图可控,外部依赖不超过三五个,且基本都是纯函数库。
- 不在热更新链路内,不需要对这段代码做频繁的HMR开发。
- 被多个业务入口或子应用复用,值得做一次独立缓存。
反过来,如果你要拆的模块里混入了import './style.css'、读取process.env、隐式依赖__dirname,或者内部引用的npm包本身带有比较重的作用域环境,那拆到Rollup里就会变成事故现场。前期可以用一个很简单的脚本扫描一下源码目录,确认是否混入非JS资源引用,再决定要不要拆:
bash复制rg "from ['\"].*\.(css|scss|svg|png|wasm)" packages/core-utils/src
这条命令输出为空时才值得继续往下走。这不算什么高科技,但能帮你把最危险的交叉依赖挡在方案之外。
1.3 适合这套方案的工程画像与收益预期
不是所有Webpack项目都需要再加一套Rollup构建,如果你的应用只有两三百个模块,连Webpack本身的编译压力都不大,引入Rollup纯属给自己找活干。从我的实战经验来看,适合上这套方案的工程一般长这样:
- Webpack生产构建时解析的模块数量超过2000,且其中有相当一部分属于长期不变的内部工具代码。
- 核心逻辑包被两个以上入口使用,每次业务构建都要重复编译它。
- 产物体积里有一块明显属于“纯JS逻辑”但很难被Tree Shaking剪干净的区域。
- 团队内部有多个前端应用,共用同一个计算、协议、校验或数据转换核心。
这套方案能带来的收益一般体现在三个层面:Webpack需要分析的模块数量减少;最终产物剥离了重复的模块包装和死代码;团队因为核心逻辑独立成包,被迫把接口边界定义得更清楚,长期维护性会变好。至于具体数据能优化到什么程度,我在第4节会贴出一个参考工程的实测记录。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 落地架构设计:核心模块拆成独立子工程
2.1 三种集成模式对比,自己选,别跟风
想把Rollup和Webpack放进同一套工程里,可选的方式并没有那么多,我实测下来比较靠谱的有三种,整理成表格方便你对照:
| 集成模式 | 做法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| A. 独立双构建 | npm script先跑Rollup,再跑Webpack主构建 | 稳定、CI友好、失败定位直观 | 多一次构建调度,流程稍长 | 大多数中后台项目 |
| B. Webpack loader内嵌 | 写一个loader,在Webpack编译某段代码时 |
