Vite 是基于浏览器原生 ES 模块规范实现的 Dev Server,无论是应用代码, 还是第三方依赖的代码,理应符合 ESM 规范才能够正常运行。但是我们没有办法控制第三方依赖的打包规范。还有相当多的第三方库仍然没有 ES 版本的产物,比如大名鼎鼎的react。这种 CommonJS 格式的代码在 Vite 中无法直接运行,我们需要对其转换成 ESM 格式的产物。
-请求瀑布流问题: 比如说,知名的loadsh-es库本身是有 ES 版本产物的,可以在 Vite 中总结运行。但实际上,它在加载时会发出特别多的请求,导致页面加载的前几秒都处于卡顿状态。
每个import都会触发一次新的文件请求,因此在这种依赖层次深、涉及模块数量多的情况下会触发成百上千个网络请求,巨大的请求量加上Chrome对同一个域名下只能同时支持6个 HTTP 并发请求的限制,导致页面加载十分缓慢,与 Vite 主导性能优势的初衷背道而驰。不过,在进行依赖的预构建之后,lodash-es这个库的代码被打包成了一个文件,这样请求的数量就会骤然减少,页面加载也快了不少。
1. Vite 是一个提倡No-bundle的构建工具,相比传统的 Webpack,能做到开发时的模块按需编译,而不用先打包再加载。
模块代码分为两部分:
所谓的No-bundle只是对于源代码(业务代码)而言,对于第三方依赖而言,Vite 还是选择 bundle(打包),并且使用速度极快的打包器 ESbuild 来完成。
为什么需要预构建?
-请求瀑布流问题: 比如说,知名的loadsh-es库本身是有 ES 版本产物的,可以在 Vite 中总结运行。但实际上,它在加载时会发出特别多的请求,导致页面加载的前几秒都处于卡顿状态。
每个import都会触发一次新的文件请求,因此在这种依赖层次深、涉及模块数量多的情况下会触发成百上千个网络请求,巨大的请求量加上Chrome对同一个域名下只能同时支持6个 HTTP 并发请求的限制,导致页面加载十分缓慢,与 Vite 主导性能优势的初衷背道而驰。不过,在进行依赖的预构建之后,lodash-es这个库的代码被打包成了一个文件,这样请求的数量就会骤然减少,页面加载也快了不少。
总结:依赖预构建主要做了两件事情:
而这两件事全部由性能优异的ESbuild(基于 golang 开发)完成,而不是传统的 Webpack/rollup,所以也不会有明显的打包性能问题,反而是 Vite 项目启动飞快的一个核心原因。
如何开启预构建
Vite 中有两种开启预构建的方式,分别是自动开启和手动开启。
我们会发现 Vite 的项目的根目录下的node_modules中发现.vite目录,这就是预构建产物文件存放的目录。
对于依赖的请求结果,Vite 的 Dev Server 会设置强缓存:
Cache-Control: max-age=3153600,immutable缓存过期时间被设置为一年,表示缓存过期前浏览器对预构建产物对请求不会再经过 Vite DevServer,而是直接用缓存的结果。
除了 HTTP 缓存,Vite 还设置了本地文件系统的缓存,所有的预构建产物默认缓存在node_modules/.vite目录中。如果一下 3 个地方都没有改动过,Vite 将一直使用缓存文件:
package.json 的dependencies字段
各种包管理器的 lock 文件
optimizeDeps配置内容
因为 Vite 中预构建中本地文件系统的产物缓存机制,而少数场景下我们不希望用本地的缓存文件,比如需要调试某个包的预构建结果,有以下方法可以清除缓存:
删除node_modules/.vite目录
在vite配置文件中,将server.force设为true。(在Vite3.0后中配置项需要将optimizeDeps.force设为true。
命令行执行npx vite --force 或者 npx vite optimize。