1. 何为vite
法语意为 "快速的",发音 /vit/
,同 "veet"。
是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成:
- 一个开发服务器,它基于 原生 ES 模块 (ESM)提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
- 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
2. 为什么选Vite
1)性能瓶颈
构建越来越大型的应用时,需要处理的 js 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。基于 js 开发的工具就会开始遇到性能瓶颈。通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。
2)开发环境基于打包器的方式已经落伍
基于打包器的方式启动必须优先抓取并构建你的整个应用,然后才能提供服务。
基于打包器启动时,重建整个包的效率很低。原因显而易见:因为这样更新速度会随着应用体积增长而直线下降。
3)浏览器 ESM 支持
浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。
在开发环境下,Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
3. Vite 与 webpack 主要差异
Webpack | Vite |
---|---|
先打包生成bundle,再启动开发服务器 | 先启动开发服务器,利用新一代浏览器的ESM能力,无需打包,直接请求所需模块并实时编译 |
热更新(HMR)时需要把改动模块及相关依赖全部编译 | 热更新时只需让浏览器重新请求该模块,同时利用浏览器的缓存(源码模块协商缓存,依赖模块强缓存)来优化请求 |
4.Vite 开发环境构建性能高原因
Webpack 从入口开始需要逐步经历语法解析、依赖收集、代码转译、打包合并、代码优化,最终将高版本的、离散的源码编译打包成低版本、高兼容性的产物代码,这可满满都是 CPU、IO 操作啊,在 Node 运行时下性能必然是有问题。而热更新(HMR)时也很慢,即使只有很小的改动,Webpack依然需要构建完整的模块依赖图,并根据依赖图来进行转换。
Vite 开发环境编译快:
- 开发环境冷启动无需打包,只是使用esbuild对依赖进行预构建,将CommonJS和UMD发布的依赖转换为浏览器支持的ESM
- 无需分析模块之间的依赖,无需在启动开发服务器前进行编译
- 在一开始将应用中的模块区分为 依赖 和 源码 两类,源码模块协商缓存,依赖模块强缓存(依赖大多为开源的不会变动的node_modules模块,请求路径满足 /^\/@modules\// 格式就会被认为是依赖)
Vite 热更新更快:
- 利用了ESM和浏览器缓存技术,构建后的依赖请求(http头的max-age=31536000,immutable)进行强缓存,以提高页面性能。
- 更新速度与项目复杂度无关。
5. 预构建原理
Vite 对 js/ts 的处理没有使用如 glup
, rollup
等传统打包工具,而是使用了 esbuild
。esbuild
是一个全新的js打包工具,底层使用了go,大量使用了并行操作,可以充分利用CPU资源。esbuild
支持如babel, 压缩等的功能。esbuild
比 rollup
等工具快十几倍。
预构建:
当你首次启动 vite
时,使用esbuild
在启动开发服务器前把检测到的依赖进行预构建。
- 将作为 CommonJS 或 UMD 发布的依赖项转换为 ESM。
- 将有许多内部模块的 ESM 依赖关系转换为单个模块,以提高后续页面加载性能(预先把一个模块用到的所有内部分支模块全部打包成一个bundle),这样就浏览器在请求某个模块时,便只需要发送一次请求了。
- 对部分文件如 tsx 进行类型解析,转换为js
当开发服务器启动后,除非遇到一个新的依赖关系导入,而这个依赖关系还没有在缓存中,Vite 将重新运行依赖构建进程并重新加载页面。
Vite 的基本实现原理:
当浏览器解析 import HelloWorld from './components/HelloWorld.vue'
时,会向当前域名发送一个请求获取对应的资源(ESM支持解析相对路径)。
就是启动一个 koa 服务器拦截由浏览器请求 ESM的请求。通过请求的路径找到目录下对应的文件做一定的处理最终以 ESM的格式(响应类型为js)返回给客户端。
客户端注入本质上是创建一个script标签(type='module'),然后将其插入到head中,这样客户端在解析html是就可以执行代码了。
浏览器下载对应的文件,然后解析成模块记录。接下来会进行实例化,为模块分配内存,然后按照导入、导出语句建立模块和内存的映射关系。最后,运行上述代码,把内存空间填充为真实的值。
静态资源加载
当请求的路径符合 image, media, fonts 或 JSON 格式,会被认为是一个静态资源。静态资源将处理成ESM模块返回。
vue文件
解析vue文件是实时的,是实时编译的。不会预先编译。需要配置@vitejs/plugin-vue
插件。编译时使用Vue中的@vue/compiler-sfc
将Vue文件编译为AST语法树,抽离其中的 template, css, script。使用@vue/compiler-dom
处理template模板转为js。
当 Vite 遇到一个 .vue 后缀的文件时。由于 .vue 模板文件的特殊性,它被拆分成 template, css, script 模块三个模块进行分别处理。最后会对 script, template, css 发送多个请求获取。
如上图中请求 App.vue
获取 script 代码,App.vue?type=template
获取 模板;App.vue?type=style
获取样式。这些代码都被插入在 App.vue 返回的代码中。
js/ts处理
Vite使用esbuild将ts转译到js,约是tsc速度的20~30倍,同时HMR更新反应到浏览器的时间会小于50ms。但是,由于esbuild转换ts到js对于类型操作仅仅是擦除,所以完全保证不了类型正确,因此需要额外校验类型,比如使用tsc --noEmit。
将ts转换成js后,浏览器便可以利用ESM直接拿到js资源。
6. 热更新原理
在客户端与服务端建立了一个 websocket 连接,当代码被修改时,服务端发送消息通知客户端去请求修改模块的代码,完成热更新。
Vite 中客户端的 websocket 相关代码在处理 html 时被写入代码中。Vite 会接受到来自服务端的消息。通过不同的消息触发一些事件。做到浏览器端的即时热模块更换(热更新)。
7. 为什么生产环境仍需打包
尽管原生 ESM 现在得到了广泛支持,但由于嵌套导入会导致额外的网络往返,在生产环境中发布未打包的 ESM 仍然效率低下(即使使用 HTTP/2)。
对比在开发环境Vite使用esbuild来构建依赖,生产环境Vite则使用了更加成熟的Rollup来完成整个打包过程。因为esbuild虽然快,但针对应用级别的代码分割、CSS处理仍然不够稳定,同时也未能兼容一些未提供ESM的SDK。
为了在生产环境中获得最佳的加载性能,仍然需要对代码进行tree-shaking、懒加载以及chunk分割(以获得更好的缓存)。
8. 构建工具和打包工具的区别?
构建过程应该包括 预编译、语法检查、词法检查、依赖处理、文件合并、文件压缩、单元测试、版本管理等 。
打包工具更注重打包这一过程,主要包括依赖管理和版本管理。
9. Vite有什么缺点?
- 目前 Vite 还是使用的 es module 模块不能直接使用生产环境(兼容性问题,如果你的项目不需要兼容 IE11 等低版本的浏览器,自然是可以使用的)
- 生产环境使用 rollup 打包可能会造成开发环境与生产环境的不一致。
- 很多 第三方 sdk 没有产出 ESM格式的的代码,这个需要自己去做一些兼容。目前支持 CommonJS(CJS)代码快速转化为 ESM,但是对于一些格式不规范的代码,可能还是需要单独处理。
10. 动态导入多个组件
使用import.meta.glob
方法:
// 1.上面的方法相当于一次性加载了 views 目录下的所有.vue文件,返回一个对象
const modules = import.meta.glob('../views/*/*.vue');
const modules ={
"../views/about/index.vue": () => import("./src/views/about/index.vue")
}
// 2.动态导入的时候直接,引用
const router = createRouter({
history: createWebHistory(),
routes: [
// ...
{
path: 'xxxx',
name: 'xxxxx',
// 原来的方式,这个在开发中可行,但是生产中不行
// component: () => import(`../views${menu.file}`),
// 改成下面这样
component: modules[`../views${filename}`]
}
// ...
],
})
其他基础可看文档...
本文固定连接:https://code.zuifengyun.com/2022/09/2744.html,转载须征得作者授权。