Design flaws
1. Transform Hook
rollup 的钩子判断和执行逻辑均在 transform 钩子中,也就是说每一个钩子的 transform 钩子都会为每一个模块执行一遍。这对于原生 bundler 来说并不可取,若原生 bundler 也借鉴了该设计,那么会存在性能问题。原生 bundler 会频繁与 javascript 通讯并调用 javascript 的 transform 钩子,即使这个模块的处理逻辑不在这个插件的 transform 钩子中。原生 bundler 调用单线程的 javascript 是十分昂贵的性能开销,通信开销在上万个模块通信场景下将会十分巨大,尤其是在 HMR 下,这将更难以接受。
以 esbuild 为例,模块需要先经过 filter 正则逻辑的过滤后,允许的正则表达式语法是 go 的正则表达式引擎支持的语法。这与 javascript 略有不同,go 的正则表达式并不支持前瞻(look-ahead)、后顾(look-behind)和反向引用(backreference)。go 的正则表达式引擎设计目的是避免部分会导致 javascript 灾难性指数时间最坏情况的性能问题。若命中 filter 逻辑,则 go 会与 javascript 通信并回调 javascript 的 transform 钩子,性能开销低的同时避免了大量 go 端与 javascript 端的通信。
在 Bundler的设计取舍 一文中也提到 rspack 也认同了 esbuild 的插件与原生通讯设计。
2. Incremental Build
rollup 增量更新类似于 react 的 fiber 树的增量更新,自顶而下的进行检测,对于入口模块每次都会调用 resolveId,同时对于每一个模块都会执行 load 钩子,这的确与初始冷启动相比减少了 resolveId 和 transform 钩子的执行次数。但对于每一个模块都需要执行 load 钩子,这在大量模块的场景下,是十分消耗性能的。
从 rollup issue 2182、rollup issue 3728 中可以看到,rollup 目前对于硬盘空间上的持久性缓存(Persistent Cache)还不支持,也就是说 rollup 目前只支持对于 watch 模式下的增量更新,而不支持再次冷启动时的增量更新。webpack 支持了 Persistent Cache,这也是 webpack 在二次冷启动上胜过 rollup 的原因之一。
