官网:https://cn.vitejs.dev/
在开发初期,项目体量比较小的时候,构建优化的效果可能还不太明显,而随着多人协作开发导致项目体量的增大,开发阶段将项目跑起来,也就是通过 npm run serve 的单次冷启动耗时会越来越久。以Webpack为例,它的大致构建流程(Bundle的构建过程)如下:
基于入口文件不断寻找依赖逐个编译再递归处理,每次递归都需要调用所配置的Loader(javascript)规则进行编译,经历 String->AST->String 流程,直到入口依赖的所有文件都经过处理才构建完成。同时,由于 NodeJS 单线程的特性以及语言本身的效率限制,Webpack 构建慢一直成为它饱受诟病的原因。
构建Bundle完成之后,才算完成启动开发服务器的准备工作。访问链接时,直接请求编译好的文件,并通过source-map映射到源码进行调试。
当修改文件时,HMR 也是需要把改动的模块代码及相关依赖全部编译后,才会更新界面。
这也是为什么项目代码量越来越大的时候,项目启动时间会变的越来越长,而且稍微改一点代码,也会好长时间才热更新界面。
优化:缓存(babel-loader等)、多进程打包(thread-loader)、分模块构建(启动项目时增加一些配置命令,在初始化时只引入该业务所需要的路由模块,如下图拆分出多个路由)等。
Vite(法语“快速”) 是一种构建工具,旨在利用浏览器生态中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写,提供快速的开发体验。
Vite 的 Github 仓库累计获得了超过 60k Star。
Vite 每周的下载量达到了 720 万次,相比去年增长了 4 倍。
Angular v16的支持
Vite 通过在一开始将应用中的模块区分为 依赖 和 源码 两类,改进了开发服务器启动时间。
node_modules/.vite
中,主要解决两个问题 1. 依赖也通常会存在多种模块化格式(例如 ESM 或者 CommonJS)需要转换为浏览器支持的 ES 模块,2.具有许多内部模块的 ESM 依赖项转换为单个模块,减少网络请求次数。esbuild比以 JavaScript 编写的打包器(webpack)预构建依赖快 10-100 倍。以 原生 ESM 方式提供源码,这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
主流打包器都支持动态模块热替换(HMR),这大大改进了开发体验 —— 然而,在实践中发现,其热更新速度也会随着应用规模的增长而显著下降。以Webpack为例,当修改文件时,会通过ws告知浏览器有哪些变化,浏览器就会更新我们的代码,完成局部刷新。该过程的主要耗时点在于,会以当前修改的文件为入口重新 build 打包,所有涉及到的依赖也都会被重新加载一次。
由于vite使用unbundle机制,所以dev server监听到文件变化之后,只需要通过ws通知浏览器去重新加载变化的文件,剩下的工作就交给浏览器去做。使得无论应用大小如何,HMR 始终能保持快速更新。
Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求。
使用 express 搭建简易ES Module文件请求,来理解vite对源码的处理流程
页面能正常展示,并且按照顺序依次引入esmodule文件(忽略掉浏览器插件引入的文件)。
yarn create vite my-vue-app --template vue
npx degit user/project#main my-project
或 git clone -b develop git@xxx.org:username/repo.git --depth=1
Vite插件沿用了 Rollup 部分的钩子函数,并带有一些独有的钩子。所以插件分为两种: Vite 的专属插件、兼容 Rollup 的插件 。官网模版搭配其自带特性基本可以直接进入开发,剩下的就是资源优化和第三方依赖。
以简单react为例,执行npm create vite@latest
选取React其余默认即可
生成后的目录结构,vite默认的入口文件是根目录下的index.html
,其中引入<script type="module" src="/src/main.jsx"></script>
,esbuild以此开始进行预构建,服务器启动很快,并在node_module/.vite
目录下生成构建产物。
以import ReactDOM from 'react-dom/client'
依赖为例,讲解vite对依赖项的处理
源码以commonjs形式导出,并且引入了子文件,子文件又引入其他子文件
再来看下构建产物,以esmodule导出(转成浏览识别的esmodule),并且所有依赖项及子依赖项的代码经过处理全部包含在产物文件中(将依赖项的多个模块转成单个模块,减少网络请求)
页面能正常展示,F12查看请求的资源文件,以localhost
为入口,按顺序依次加载其他资源。其中引入的依赖路径会修改为预编译后文件夹下mode_modules/.vite/deps
的文件,源码路径不会改变。
对现有webpack react 项目改造为vite启动,体验下
webpack启动项目时长:16996ms
vite启动时长:184ms
https://cn.vitejs.dev/guide/build
vite提供命令vite build
用于构建生产环境产物,采用了 Rollup 灵活的插件 API 和基础建设,这对 Vite 在生态中的成功起到了重要作用。但是也存在部分问题(官网有提到),要确保开发服务器和生产环境构建之间的最优输出和行为一致并不容易。
esbuild 非常快,但是对构建资源优化的控制非常有限,并且插件API不够灵活;
Rollup 成熟且灵活,但与本地打包工具相比仍然较慢,并且ESM/CJS互操作的处理还有待改进。
目前正在构建一个名为 Rolldown 的 Rust 版本的 Rollup,一旦 Rolldown 准备就绪,它就可以在 Vite 中取代 Rollup 和 esbuild,显著提高构建性能,并消除开发和构建之间的不一致性。
这些问题大多数从 Vite 发布时就存在了。挑战在于这些问题大多需要在打包工具层次上解决。Vite 在底层使用了两个打包工具:esbuild 和 Rollup。
这两种打包工具无法完全替代彼此,并且存在一些细微的行为差异。所以,如果能有一个具有本地速度和 Rollup 的灵活性的打包工具,那太好了。
这个打包工具就是 Rolldown。
rollup,vite以及webpack比较与介绍:https://juejin.cn/post/7097493230572273700
手把手系列,实现一个简单的 vite开发版:https://juejin.cn/post/7020342255080718372
vite插件推荐:https://juejin.cn/post/7256723839941476412#heading-16
前端构建效率优化之路:https://www.cnblogs.com/coco1s/p/16542895.html
vite 为什么比 webpack 快?https://www.cnblogs.com/beileixinqing/p/16735190.html
vite2 源码分析(一) — 启动 vite:https://juejin.cn/post/7084634741713928199