Vite 底层所深度使用的两个构建引擎——Esbuild
和Rollup
Esbuild
依赖预构建——作为 Bundle 工具
主要是 ESM 格式的兼容性问题和海量请求的问题,对于第三方依赖,需要在应用启动前进行打包并且转换为 ESM 格式。
Esbuild打包工具的缺点
- 不支持降级到
ES5
的代码。这意味着在低端浏览器代码会跑不起来。 - 不支持
const enum
等语法。这意味着单独使用这些语法在 esbuild 中会直接抛错。 - 不提供操作打包产物的接口,像 Rollup 中灵活处理打包产物的能力(如
renderChunk
钩子)在 Esbuild 当中完全没有。 - 不支持自定义 Code Splitting 策略。传统的 Webpack 和 Rollup 都提供了自定义拆包策略的 API,而 Esbuild 并未提供,从而降级了拆包优化的灵活性。
尽管Esbuild有以上这些缺点,但是不妨碍Vite 在开发阶段使用它成功启动项目并获得极致的性能提升,生产环境处于稳定性考虑当然是采用功能更加丰富、生态更加成熟的 Rollup 作为依赖打包工具了。
单文件编译——作为 TS 和 JSX 编译工具
在依赖预构建阶段, Esbuild 作为 Bundler 的角色存在。而在 TS(X)/JS(X) 单文件编译上面,Vite 也使用 Esbuild 进行语法转译,也就是将 Esbuild 作为 Transformer 来用。
代码压缩,作为压缩工具使用
在生产环境中 Esbuild 压缩器通过插件的形式融入到了 Rollup 的打包流程中
传统的方式都是使用 Terser 这种 JS 开发的压缩器来实现,在 Webpack 或者 Rollup 中作为一个 Plugin 来完成代码打包后的压缩混淆的工作。但 Terser 其实很慢,主要有 2 个原因。
-
压缩这项工作涉及大量 AST 操作,并且在传统的构建流程中,AST 在各个工具之间无法共享,比如 Terser 就无法与 Babel 共享同一个 AST,造成了很多重复解析的过程。
-
JS 本身属于解释性 + JIT(即时编译) 的语言,对于压缩这种 CPU 密集型的工作,其性能远远比不上 Golang 这种原生语言。
Esbuild 这种从头到尾共享 AST 以及原生语言编写的 Minifier 在性能上能够甩开传统工具的好几十倍。
Vite 将 Esbuild 作为自己的性能利器,将 Esbuild 各个垂直方向的能力(Bundler
、Transformer
、Minifier
)利用的淋漓尽致,给 Vite 的高性能提供了有利的保证。
Rollup
生产环境 Bundle
ESM 已经得到众多浏览器的原生支持,但生产环境做到完全no-bundle
也不行,会有网络性能问题。为了在生产环境中也能取得优秀的产物性能,Vite 默认选择在生产环境中利用 Rollup
打包,并基于 Rollup 本身成熟的打包能力进行扩展和优化,主要包含 3 个方面:
-
CSS代码分割,异步模块中的引入的CSS代码,Vite会自动将这些代码生成一个CSS文件,提高缓存利用率。
-
自动预加载,Vite会自动为入口chunk的依赖自动生成预加载标签
<link rel="moduelpreload">