1.概述
最近在看优化js构建效率的东西,我目前的项目,生产环境是基于webpack4做的打包构建,开发环境是基于vue-cli4做的打包构建(目前项目中使用的vue-cli构建也是基于webpack4完成的,只不过配置文件是用的vue.config.js,而不是用的webpack原装配置),总觉得构建的时间有点儿长,虽然对目前项目来说不是什么大问题,但如果能够有所改进还是更好的。
基于改善vue项目构建时长这个出发点,我先后调研了vite/esbuild/swc,虽然调研的结论是我目前不改构建配置更好,但调研让我对新的构建工具有了更多的了解,因此在这里记录一下。
2. Webpack/Vite/Esbuild/Swc/Rollup之间的关系
2.1 工具层次
前端涉及到构建层面的工具,从上往下分为三层:
- 项目脚手架:比如
vue-cli,这是构建层面的最外层,是一套帮助用户快速搭建项目的完整系统,通过简单的指令,可以快速生成一套包含了基本项目目录、构建工具、语法编译器、本地服务工具等开箱即用的代码 - 模块打包器:比如
webpack、vite、esbuild,用户通过这些工具暴露出来的配置API,来将babel、webpack-dev-server这类具体做某件事的工具通过模块化的方式连贯起来完成一整套构建流程 - 构建中依赖的具体工具:比如
babel(语法编译器)、webpack-dev-server(本地服务工具)、sass-loader(sass语法转换器)等等
2.2 工具定位
下面详细解释一下不同工具在构建中的定位:
vue-cli是脚手架,是构建层面的最外层,不使用脚手架也可以实现完整的构建方案,脚手架只不过是给用户提供了基本的开箱即用的方案,在终端执行脚手架指令,就会进入脚手架的选择界面,在界面中选好自己需要的基本搭配,就会自动生成一套开箱即用的代码,非常灵活方便。
webpack/snowpack/rollup/vite/esbuild/gulp/grunt/fis这类,才是真正的构建工具(模块打包器),其中gulp/grunt/fis在发展过程中逐渐被边缘化,用的人越来越少了,就不再赘述,当然它们也很好,在构建工具的发展历史中起到了重要作用,即便是今天也可以完美的满足很多中小型项目的需求。
webpack目前在前端构建工具领域独占鳌头,暂时还没有能彻底打败替换它的产品出现,其插件生态非常强大且成熟,已经涵盖了绝大部分的应用场景。
rollup比webpack出现的稍微晚一点儿,是最早实现了利用ESM打包方式完成摇树优化的工具(webpack后来也实现了),且所有模块构建在一个函数内,执行效率理论上要比webpack更高一点儿,不过webpack随着版本的不断优化和越来越强大的插件生态,现在两个工具的打包效率应该是差不多的。
snowpack/vite/esbuild都是近两、三年的后起之秀,他们的卖点主要就是快。snowpack利用了现代浏览器的本身的module系统,跳过复杂的模型之间的组织编译过程而只关注于变更文件本身的编译,因此从处理逻辑上,就远远快于webpack这类工具。esbuild是使用golang实现的工具,可以最大化的利用线程并行且可直接编译成机器码,所以其速度远远快于基于js编写的webpack就完全可以理解了。vite在开发环境下是基于esbuild来完成打包的,生成环境下是基于rollup来完成打包的,因此开发环境下构建速度远远快于webpack,是毫秒级的,而生成模式下构建速度也不慢于webpack。
swc的定位不是构建工具,它的主要作用是语法编译器,也就是主要用来替换babel的。它的主要卖点也是快,其是基于近两年大火的语言Rust开发的,执行效率跟golang可一较长短,自然比基于js开发的babel要快得多。
还有一个基于webpack的插件esbuild-loader,它是esbuild的精简版,主要作用是取代babel做语法编译,同时附带一些打包压缩功能,比babel要稍微快一点儿,但因为本身就是个为了配合webpack使用的精简版产物,所以功能远没有babel齐全,对装饰器语法的编译效果也不好,如果项目编译速度不是主要痛点,不建议使用,有点儿得不偿失。
说了那么多,这里可以把上面提到的工具分个类:
- 脚手架:
vue-cli - 构建工具(模块打包器):
webpack,rollup,snowpack,esbuild,vite - 语法编译器:
babel,swc,esbuild-loader
3. 放弃修改构建配置的原因
之前基于vite做的demo,在开发环境下那毫秒级的打包体验确实相当惊艳,因此就想试着能不能改变现有的打包配置,来提升打包速度,增加体验。
现在最新版的vue-cli,可以在vite和webpack中选一个作为构建工具,但是一下子从webpack替换到vite,感觉步子迈的有点儿大,且vite目前的生态支持也远没有webpack那么强大,贸然替换风险较高,因此想要先从更小的维度看看能不能优化。
因为vite的开发环境打包是基于esbuild完成的,就想着esbuild在webpack中能不能用,一查发现还真有个esbuild-loader插件可以供webpack使用,说是可以用来替换babel,增加10%到30%的语法编译速度,于是我就从npm拉取下来试了试,发现在alias中设置的路径别名始终无法被识别,百度了半天也找不到怎么设置,然后在查询esbuild-loader使用的时候,还发现很多人都说esbuild-loader对于装饰器语法的支持也不好,而我们的项目里面恰恰使用了装饰器语法,所以我最终放弃了使用esbuild-loader的打算,且esbuild-loader并不能极大的改善打包速度,但却因此要冒着巨大测试风险,太过得不偿失。
放弃esbuild-loader后,我又把目光放在了基于Rust的swc-loader上,因为swc的定位是取代babel,所以相比于esbuild-loader,似乎更适合我的需求。但是swc的生态没有babel强大,且swc的压缩能力太弱,远没有babel生态中各种各样好用的压缩工具,因此最终我也放弃了使用swc-loader的打算。
我在权衡完自己项目的实际情况后,决定目前不再打修改构建配置的主意,不过或许有些项目就很在意打包效率这一块,那么不妨看一看我说的这几种工具,说不定就适合你的项目呢。
4. 参考文献
webpack4 react使用esbuild-loader
webpack 或 esbuild:为什么不是两者兼而有之?