Rolldown 下一个版本会把插件改动的回传做细,只把真的改了的字段送回 Rust,大型项目里这一步从 1015 毫秒降到 39 毫秒。
消息是 Rolldown 维护者 hyfdev 公布的,改动来自 anonrig,也就是 Yagiz Nizipli,现在负责 X 的 Web 前端。
先说清楚这里省的是什么。
Rolldown 用 Rust 写打包核心,插件生态还是 JavaScript,两边要来回传数据。
generateBundle 和 writeBundle 这两个输出钩子跑完之后,插件对产物做的修改要送回 Rust,才能写进最终文件。
旧做法很粗暴,插件动了 chunk 上任何一个字段,整个 chunk 就跟着所有属性一起送回去。

代价在哪儿呢,PR 里写得很细。
Rust 把 chunk 的 sourcemap 序列化一遍,JavaScript 解析一遍,Rust 再解析一遍。
code、moduleIds、exports 这些字段双向拷贝,而 Rust 那边的 update_output_chunk 压根不看大部分。
更麻烦的是缓存,Rust 替换 chunk 数据之后,之前那份代码缓存就废了,下一个读 chunk.code 的插件又得把代码整个拷一次。
Vite 自己就踩在上面,只要有纯 CSS 的 chunk,vite:css-post 会对每个 chunk 执行一次 chunk.imports = chunk.imports.filter(...),而大多数 chunk 的文件名根本没变。
所以改法很直接,只送变了的字段,没变的 chunk 一个字节都不发。
改完照样能用,就地修改读到的 imports 数组、把 map 设成 null、改名、删产物这些情况都保留了原来的语义。
作者还加了护栏,往 chunk.code 里写非字符串、往 imports 里写非数组,现在直接抛 TypeError 并说明是哪个 chunk 的哪个属性,不会再静默按旧值写出去。
快多少呢。

PR 里跑的是交错 A/B,基准是 2182 个 chunk、30 MB 压缩后的 JS、136 MB 隐藏 sourcemap 的构建。
generate() 那一轮,整个 generateBundle 阶段从 1256 毫秒降到 752 毫秒,少了 40.1%,总时间从 2392 毫秒降到 1862 毫秒。
另外两轮分别是 1400 降到 798 毫秒、1036 降到 644 毫秒,总时间少了 20% 到 25%。
每轮跑出来的产物哈希都一样。
真实应用那边更夸张。

拿那个大型 Vite 应用自己的生产构建跑了两轮 CPU 采样,回传这一段 collectChangedBundle 从 1015 毫秒和 720 毫秒,降到 39 毫秒和 28 毫秒。
读 chunk.map 的时间从 866 毫秒降到 97 毫秒,读 chunk.code 从 387 毫秒降到 189 毫秒。
hyfdev 自己补了一句,26 倍只是「准备改动」这一步,不是整个钩子。整段输出钩子快 40% 左右,这个倍数不能当全局数字用。
产物没变这件事也核对过,把哈希归一化之后,10958 个文件里除了 5 个全都一致。
那 5 个两次基线构建之间也不一样,一个是应用每次都会重新混淆的脚本,一个是 client manifest。
什么时候能用上呢,改动已经在 11 月 6 日合并,会跟着下一个版本走,Vite 8 用的就是 Rolldown。
如果你写的是中小型项目,感知不会明显,chunk 少,钩子也少。
真正受影响的是那种几千个 chunk、开着隐藏 sourcemap 的大项目,generateBundle 一跑就要几秒,插件还得按环境拆开来写。
这次没有动钩子对象本身,只是把「回传」这件事做细了,先看哪些字段变了,再决定传什么。
PR 地址:https://github.com/rolldown/rolldown/pull/11488。
你项目里的构建时间卡在哪儿,欢迎在评论区说说。
关于我:
- 农村程序员(
2020年回农村,至今未出村) - 独立开发者(
致力于开发 1000 个小产品) - 技术观察者(
分享编程技术,行业资讯等) - 前端之虎陈随易(
自封的) - 我的官网是:
https://yicode.tech