451 - 《Mako 提升构建速度的 N 种方法》
本文是 2024.06.28 在 SECon 上分享的主题的第三部分的文字稿初稿,正式版略有调整,第二部分见 449 - 《从 0 实现 Rust 构建工具》。
SECon 上分享的 PPT 见 https://drive.google.com/file/d/1-MllUZnkk45_58vLxXxOLz4VFxgyGADN/view?usp=sharing 。
1、提速的意义。
先聊一些湿货吧。提速的意义我觉得有两个,1)第一个大家肯定都知道,速度快了,开发体验好,研发效率提升,2)第二个功能侧的意义,因为快,我们拥有了能做之前不能做的能力。
如果大家熟悉前端社区,之前构建有个俗成的约定是,不对 node_modules 下的依赖库做 Babel 编译,因为 node_modules 下的文件数太多,所以慢。但是,不少依赖库是会使用高级语法的。于是就有了右图的这个库,用于标记哪些依赖的哪些版本用了高级语法,再对这些库做编译。然而显然,这种白名单标记的方式,总是事发后才会去做。
2、
在提速之前,还要准备几件事。第一件是 Benchmark,得数据驱动。不然什么时候性能劣化了都不知道。这在前面有过介绍,这里就不赘述了。
3、
第二件事是工程化。有合适的工具,绝对会事半功倍。而且提速这件事,不能盲目地做,得找到性能卡点,针对性地做优化。避免过早优化,同时避免为了优化性能而引入不必要的复杂度。通常性能和简单的设计并不冲突。
我们用过的性能相关工具有 XCode Instruments、Puffin 和 cargo bench 等。右图是通过 XCode Instruments 发现的一处问题,红色圈圈圈起来的地方本应该可以并行执行的,现在却是串行的,优化完之后,右边的执行时机就可以往左提了。
4、
在开发 Mako 之前,我们其实也已有多年和 Webpack 斗智斗勇的提速经验。并且总结了一些行之有效的方法论。1)并发,这在 Rust 里应该是提速的关键,同时在 Node 侧其实也可以利用 Worker 发挥下,我们做的 Less in Worker 就是这类,2)缓存,Mako 中也有大量应用,以及之前我们做的基于 Webpack 的 MFSU 方案,Webpack 的物理缓存也是此类,3)延迟执行,比如基于路由的按需编译,Vite 这种 Bundless 的打包方式,都属于此类,4)原生语言,用 Rust 写 Mako 其实就是,但除了构建本身,生态上的依赖也可利用原生语言,比如 Less 我们有想过用 Rust 重写,但出于 ROI 考虑,暂时没有做。
5、
我们有为提速专门做过脑暴,这些点其实都可以套上前一页的方法论里。然后我们按「Pig or Chicken」的方式投了自己的肉和蛋。关于