在浏览器支持 ES 模块之前,JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发。因此诞生了诸如 webpack、Rollup 和 Parcel 等工具来使我们得以获得更好的开发体验。
然而,当我们开始构建越来越大型的应用时,基于 JavaScript 开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。这无疑严重影响了前端开发者的开发效率和幸福感。
Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。
更快的服务器启动
「传统的打包工具 ⬇️」
传统的打包工具如Webpack
在启动服务器之前,需要先解析依赖、打包构建整个应用。随着我们的项目体积越来越大,服务器的启动也会越来越慢。
「那 Vite 是如何做的呢?⬇️」
如图所示,vite 上来就先起了一个服务器,直接赢在了起跑线!!!
不过别着急,光起了服务器,少了传统的构建、打包的工作,服务器上空空如也,我们的代码如何能被浏览器接收呢?
答案就是「ES Module」——ESM
是JavaScript
提出的官方标准化模块系统,可以直接在浏览器中去执行import
,动态引入我们需要的模块。
ES Module 是 Vite 的核心原理,大体流程如下:
浏览器解析到一个类型为
module
的 script 标签(代码需滑动才可见)<script type="module" src="main.js"></script>
自动发起一个 http 请求去获取 main.js(代码需滑动才可见)
// main.js import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')
发现 main.js 中还有 import,则继续发起请求获取对应的资源。
正因如此,我们无需打包整个应用,而是将依赖解析的工作交给浏览器,让浏览器去解析需要哪些文件并只对这些文件发起请求。这样一来,即使随着项目的迭代页面越来越多,但每次我们只会请求一个 route 中涉及到的模块,因此页面的构建速度丝毫不会受到影响。
更快的更新
对于传统的打包工具,热更新(HMR)会将变更的文件以及相关依赖重新编译打包,然后请求变更后模块的代码,客户端重新加载。
而在 Vite 中,HMR 是在原生 ESM 上执行的,无需打包。当有文件变更时,Vite 只需要重新对变更的文件发起请求即可,因此无论应用大小如何,HMR 始终能保持快速更新。
其他优化策略
浏览器缓存
ESM 是通过 HTTP 请求来加载模块的,那么 Vite 就可以通过 HTTP 头来加速整个页面的重新加载。
源码模块的请求会根据
304 Not Modified
进行协商缓存,而依赖模块请求则会通过Cache-Control: max-age=31536000,immutable
进行强缓存。依赖预构建
Vite 将有许多内部模块的 ESM 依赖关系转换为单个模块,以提高后续页面加载性能。
例如,
lodash-es
有超过 600 个内置模块!当我们执行import { debounce } from 'lodash-es'
时,浏览器同时发出 600 多个 HTTP 请求。通过预构建
lodash-es
成为一个模块,便只需一次 HTTP 请求。开发模式下使用 esbuild
源码的编译和依赖预构建的过程会使用 esbuild。
image-20220804140345843 ❝
Above: the time to do a production bundle of 10 copies of the three.js library from scratch using default settings, including minification and source maps.
❞
感受一下
我找了一个大小为 1G(含 node_modules)的项目,拥有 90+路由,且同时支持 webpack 和 Vite。
以下是 Webpack 和 Vite 的版本:
❝webpack@4.46.0
webpack-dev-server@3.11.3
vite@2.9.9
❞
「项目冷启动对比」
当 Vite 启动服务器后,Webpack 甚至都还没开始进行编译。
Webpack 启动完成,可以看到,Webpack 仅编译完成就耗费了 44s,这个时间完全够我去接一杯水了。
再看浏览器中的资源请求(仅 js),可以明显看出 Vite 对于模块的划分粒度很细并且没有将模块打包在一起,因此在热更新时,可以十分精确的找到对应的模块并更新。
❝Webpack
image-20220814234204519 ❞
❝Vite
将.Vue 文件拆分成了三个模块,分别是 js、style、template。
image-20220814235718297 ❞
「热更新对比」
修改文件并保存,Webpack 开始编译时,Vite 已经完成了文件的转换工作并提供给浏览器,而过了 6s 之后 Webpack 才将更新后的模块准备好。