跳至内容

为什么选择 Vite

问题

在浏览器原生支持 ES 模块之前,开发者没有原生的机制以模块化的方式编写 JavaScript。这就是为什么我们都熟悉“打包”的概念:使用工具来爬取、处理和连接我们的源模块,生成可以在浏览器中运行的文件。

随着时间的推移,我们看到了像 webpackRollupParcel 这样的工具,它们极大地改善了前端开发者的开发体验。

然而,随着我们构建越来越复杂的应用程序,我们处理的 JavaScript 代码量也急剧增加。大型项目包含数千个模块的情况并不少见。我们开始遇到基于 JavaScript 的工具的性能瓶颈:启动开发服务器通常需要花费不合理的长等待时间(有时长达数分钟!),即使使用热模块替换 (HMR),文件编辑也可能需要几秒钟才能反映在浏览器中。缓慢的反馈循环会极大地影响开发者的生产力和幸福感。

Vite 旨在通过利用生态系统中的新进展来解决这些问题:浏览器原生支持 ES 模块,以及用编译为原生语言编写的 JavaScript 工具的兴起。

缓慢的服务器启动

在冷启动开发服务器时,基于打包器的构建设置必须急切地爬取和构建整个应用程序,然后才能提供服务。

Vite 通过首先将应用程序中的模块分成两类来改进开发服务器的启动时间:**依赖项**和**源代码**。

  • **依赖项**主要是普通的 JavaScript 代码,在开发过程中很少发生变化。一些大型依赖项(例如包含数百个模块的组件库)处理起来也相当昂贵。依赖项也可能以各种模块格式(例如 ESM 或 CommonJS)提供。

    Vite 使用 esbuild 预打包依赖项。esbuild是用 Go 编写的,预打包依赖项的速度比基于 JavaScript 的打包器快 10 到 100 倍。

  • **源代码**通常包含需要转换的非普通 JavaScript 代码(例如 JSX、CSS 或 Vue/Svelte 组件),并且会经常编辑。此外,并非所有源代码都需要同时加载(例如,使用基于路由的代码拆分)。

    Vite 通过 原生 ESM 提供源代码。这本质上是让浏览器接管打包器部分的工作:Vite 只需要根据需要转换和提供源代码,当浏览器请求它时。条件动态导入后面的代码只有在当前屏幕实际使用时才会被处理。

Bundle based dev server entry ··· route route module module module module ··· Bundle Server ready
Native ESM based dev server entry ··· route route module module module module ··· Server ready Dynamic import (code split point) HTTP request

缓慢的更新

在基于打包器的构建设置中编辑文件时,由于一个显而易见的原因,重建整个包效率低下:更新速度会随着应用程序大小线性下降。

在一些打包器中,开发服务器在内存中运行打包过程,以便在文件更改时只需使模块图的一部分失效,但它仍然需要重建整个包并重新加载网页。重建包可能代价高昂,而重新加载页面会清除应用程序的当前状态。这就是为什么一些打包器支持热模块替换 (HMR):允许模块“热替换”自身而不影响页面的其余部分。这极大地改善了 DX——然而,在实践中我们发现,即使是 HMR 更新速度也会随着应用程序大小的增长而显著下降。

在 Vite 中,HMR 通过原生 ESM 执行。当文件被编辑时,Vite 只需要精确地使编辑的模块与其最近的 HMR 边界之间的链失效(大多数情况下只有模块本身),从而使 HMR 更新始终快速,而不管应用程序的大小。

Vite 还利用 HTTP 标头来加快完整页面重新加载的速度(同样,让浏览器为我们做更多工作):源代码模块请求通过 304 Not Modified 有条件地发出,并且依赖项模块请求通过 Cache-Control: max-age=31536000,immutable 强缓存,因此一旦缓存后它们就不会再次访问服务器。

一旦你体验到 Vite 的速度有多快,我们非常怀疑你是否愿意再忍受打包开发。

为什么需要为生产环境打包

即使原生 ESM 现在得到了广泛支持,但在生产环境中交付未打包的 ESM 仍然效率低下(即使使用 HTTP/2),因为嵌套导入会导致额外的网络往返。为了获得生产环境中最佳的加载性能,最好还是使用树摇、延迟加载和公共块拆分(以获得更好的缓存)来打包你的代码。

确保开发服务器和生产构建之间的最佳输出和行为一致性并非易事。这就是为什么 Vite 附带了一个预配置的 构建命令,该命令开箱即用地集成了许多 性能优化

为什么不使用 esbuild 打包?

虽然 Vite 利用 esbuild 在开发环境中预打包一些依赖项,但 Vite 并没有使用 esbuild 作为生产构建的打包器。

Vite 当前的插件 API 与使用 esbuild 作为打包器不兼容。尽管 esbuild 更快,但 Vite 对 Rollup 的灵活插件 API 和基础设施的采用极大地促成了其在生态系统中的成功。目前,我们认为 Rollup 提供了更好的性能与灵活性的权衡。

Rollup 也一直在努力改进性能,在 v4 中将其解析器切换到 SWC。并且正在努力构建一个名为 Rolldown 的 Rollup 的 Rust 端口。一旦 Rolldown 准备就绪,它就可以替换 Vite 中的 Rollup 和 esbuild,从而显着提高构建性能并消除开发和构建之间的不一致性。你可以 观看 Evan You 在 ViteConf 2023 主题演讲了解更多详细信息

Vite 与 X 有什么不同?

你可以查看 对比 部分,了解更多关于 Vite 与其他类似工具的区别。

在 MIT 许可证下发布。 (ccee3d7c)