来自 「杨劲松」 同学的内部分享。
说起前端构建,大家一定首先想到 Webpack,确实它是前端构建的老大哥了,大而全,什么场景都能满足,社区生态爆炸。但是社区里也有许多其他优秀的构建工具,他们或许不如 Webpack 那样“包治百病”,但他们都有一些独特的优势,如果在一些特定的场景你觉得使用 Webpack 太臃肿了,那你或许可以考虑下面的一些工具。
Parcel
一个号称「「0 配置」」的打包工具,开箱即用,同时默认使用 Worker 进程充分发挥多核 cpu 优势来提升构建速度,因此在打包效率上还是不错的,而且 Parcel 2.0 在 SWC 基础上用 Rust 改写了 JS/CSS Transformer,进一步提升了构建效率。
Parcel 代码实现得非常「模块化」,有非常多内置的插件来完成各种各样的工作,用户可以针对自己的需求来使用不同的内置插件,只要在 .parcelrc
文件里配置即可,parcel 会自动读取这个配置文件,不过要注意 .parcelrc
是 JSON5 格式的文件。
文件类型
与 Webpack 不同的是,在 Parcel 中,所有文件都是一等公民,一视同仁,因此不需要用户去针对不同类型的文件配置各种 Loader,Parcel 会帮你做好不同类型文件的处理。
- 支持 JS/TS/JSX/TSX,Parcel 2.0 开始使用了 Rust 实现的 JS Transformer,能更高效地进行转译,同时也支持转译到 ES5,对于 React17 新的 JSX 也能支持。另外 Minification,Tree Shaking 等也是支持的。
- 支持 CSS,功能基本上对齐 CSS Loader,还支持各种 CSS 预处理语言,支持 Tree Shaking,Minification 等。另外支持以文本形式引入 CSS 资源,方便用户手动将 css 放入 Style Tag 中,值得一提的是,Parcel2.0 还用 Rust 实现了 CSS 的 Transformer。
- 支持 HTML。
- 支持 Vue,完全支持 Vue3 语法。
- 支持图片,丰富的图片文件处理,支持图片类型的转换以及裁剪。
- ...
构建特性
- 支持 Code Splitting,不过和 esbuild 一样只能支持比较有限的分割逻辑,被多个入口引用的共用模块或者使用
import()
动态引入的模块会被分割成单独的 Chunk。 - 支持 Tree Shaking。
- 支持 Scope Hoist。
- 支持 Minification。
- 支持 Compression,可生成 Gzip 和 Brotli 两种压缩格式的产物。
- 支持内联 Bundle,即可以以文本或者其他格式引入转译后的资源,例如上面提到的以文本格式引入编译后的 CSS 文件,亦可以直接以 dataURL 的格式引入二进制文件等。
- 支持开发阶段的 DevServer,HMR 等。
- 支持浏览器缓存,产物文件名默认带上文件内容 hash。
- 支持差异化构建,默认会同时构建出 ESM 的产物以及非 ESM 的产物。
- ...
优点
零配置,告别繁琐的工程化配置,能够满足大多数场景。在 JS 和 CSS 的转译上使用了 Rust ,效率上会有所提升。
缺点
扩展性不强,几乎没有类似 Webpack 的那种开放性插件特性,因此如果遇到 Parcel 现阶段无法实现或有 Bug 的东西,用户无能为力,只能等 Parcel 去补齐。
使用对比
打包 React Threejs 项目,Webpack:
Parcel 首次构建:
Parcel 非首次构建:
Parcel 每次构建完都会生成 .parcel-cache
文件记录各种模块的依赖关系,可以大大节省后续构建的用时,不过这个缓存能力在 Webpack 5.0 也内置了,不算是什么独特的能力。
在产物体积上,双方大致打平。
总结
目前 Parcel 最大的卖点就在于无需配置,使用体验也确实不错,性能方面在使用 Rust 改造后相信未来也能得到更大的提升,开箱即用可以满足许多场景,但是封装性好带来的副作用就是扩展性差,因此对于有大量定制化构建需求的大型项目来说 Parcel 现阶段或许不算是一个很好的选择。
Rollup
Rollup 是当前流行的库打包器,它比 Webpack 晚几年出现,也是在 ESM 之后出现的,主打的特点是能够支持并且提倡开发者使用 ESM 模块语法进行开发。
文件类型
几乎只支持 JS,其他类型的文件均需要使用插件来处理。
特点
Rollup 推崇 ESM 模块标准开发,这个特点也是借助浏览器对 ESM 的支持,Rollup 打包的产物对比 Webpack 会干净很多。例如同一个项目打包产物:
Webpack 产物:
Rollup 产物:
可以看到 Webpack 产物里是有大量的诸如 __webpack_require__
之类的代码,这些都是 Webpack 自身 Polyfill 的在运行时的模块加载,就是为了让产物代码在所有浏览器都能运行,因为 wepack 出现的时候还没有 ESM ,当时的模块标准还很混乱,Webpack 抹平了差异。用 IIFE 实现模块之间的隔离,并且用__webpack_require__
__webpack_exports__
等 Polyfill 实现在浏览器环境里模拟 CJS 模块加载,所以我们用 Webpack 打包后的代码实际上更像是跑在 Webpack 给我们实现的“虚拟 Runtime”上。
而 Rollup 诞生在 ESM 模块标准出来之后,所以 Rollup 完全遵从 ESM 标准,也就不需要像 Webpack 那样做很多 Runtime Polyfill,完全把代码交给浏览器运行。对于一些项目里依赖的老旧的 CJS 的包,也可以通过插件来对这些依赖处理。
「Rollup」 「精简的产物在体积上也是要比」 「webpack」 「来的小。」
另外对于多入口打包或动态引入的包也会做分包,我们可以直接使用 [output.manualChunks](https://rollupjs.org/guide/en/#outputmanualchunks)
来自定义分包。
插件系统
Rollup 提供了从 读取参数 到 构建 到 输出产物共计 25 种 Hook,足以满足绝大多数场景,而且目前社区里的插件数量也非常多,几乎该有的都有了,因为 Rollup 本身只认识 Javascript,所以实际使用过程中我们会需要配置比较多的插件来满足我们的场景,尤其是项目文件类型比较多样的情况下。
总结
Rollup 总体而言是非常优秀的打包工具,产物精简,符合 ESM 标准,丰富的插件系统,社区生态也很不错,是个很现代化 Web Bundler。不过相应的,他需要支持 ESM 标准的浏览器,因此对于低版本浏览器也实在没办法(愿天堂没有低版本浏览器