2021年2月,友达正式上线Vite2.0,可以说是Vite的一个重要转折点。从那时起,Vite用户数量增长非常迅速。它很快达到每周100万次npm下载。同时,Vite的社区也越来越活跃,已经形成了一个非常庞大的社区生态(详见Github地址[1]),给整个前端领域带来了很多变化,比如:Nuxt3,SvelteKit、Astro、StoryBook包括Vite在内的主要前端框架都采用了Vite作为内置构建方案。基于Vite的测试工具Vitest诞生,成为替代Jest的新一代测试解决方案。现在是2022年7月,v2版本发布16个月后,Vite正式推出3.0版本。接下来,我将介绍Vite3.0带来的一些变化和未来的计划。1.全新的VitePress文档对于用户端来说,要更新框架,文档自然是最重要的部分。现在可以直接去vitejs.dev[2]站点体验文档v3版本了。目前,该文档也是使用VitePress[3]构建的。下面是深色模式下的截图:怎么样,是不是比以前好看了?不仅是Vite,Vite生态中的一些其他项目也使用VitePress来构建文档站点,例如Vitest[4]、vite-plugin-pwa[5]和VitePress[6]自己的文档。我也强烈推荐大家使用VitePress作为自己的文档构建解决方案之一。如果需要查看Vite2.0文章,也可以访问v2.vitejs.dev[7]。二、开发阶段的更新1、CLI的更新执行vite命令启动项目时,终端的界面会和以前不一样,更重要的是为了避免vite开发的端口冲突service等应用,默认的端口号从3000变成了5173。2.开箱即用的WebSocket连接策略Vite2有一个痛点,就是在代理的情况下(比如WebIDE),我们需要手动配置WebSocket才能使HMR生效。目前,Vite内置了一套更全面的WebSocket连接策略,自动满足更多场景的HMR需求。3、服务冷启动性能提升Vite3.0在服务冷启动方面做了很多工作,最大限度地提高项目启动速度。首先我们来盘点下Vite2.x阶段服务冷启动的一些问题。从Vite2.0到2.9版本,Vite会在服务启动前预先构建依赖,即使用Esbuild扫描出项目中使用的依赖(Scan),然后进行一次打包(Optimize)。这样会造成两个问题:依赖pre-build会阻塞DevServer启动,但实际上DevServer可以正常启动而不阻塞。某些Vite插件在手动注入import语句时,比如调用babel-plugin-import添加importButtonfrom'antd/lib/button',会导致Vite二次预构建,因为引入了antd/lib/button代码由Vite插件注入,是DevServer运行时发现的依赖,冷启动阶段无法扫描。所谓二次预构建包括两个步骤。一种是全量预构建所有依赖,另一种是由于依赖更新,重新加载页面加载最新的依赖代码。这会导致DevServer性能大幅下降,尤其是在新依赖较多的场景下,浏览器很容易卡住。因此,也需要尽量避免二次预施工。当时vite-plugin-optimize-persist[8]是为了解决第二次pre-build带来的问题,将DevServer运行时扫描到的依赖持久化记录下来,这样第一次pre-build就可以被察觉并避免第二次预构建发生。在2.9版本中,Vite对预构建逻辑进行了整体重构,最终效果如下:DevServer启动后,在后台执行预构建(Optimize阶段),即预构建构建不再阻止开发服务器。开始时,你只需要等待扫描阶段完成,但通常这个阶段的开销很小。如果某些依赖是在DevServer运行时才发现的,Vite会尽可能复用已有的预构建产品,尽量不进行页面重载。具体实现可以查看这个PR[9]。那个问题彻底解决了吗?事实上,它不是。在某些场景下,Vite仍然不可避免地需要进行二次预构建。比如下面的例子:A和B都是项目的第三方依赖,同时也依赖C。然后Vite在预构建A的时候,会把A和C打包在一起。但是Vite发现它在运行时依赖B,A和B需要共享C的代码,所以C的代码可能会被提取到一个commonchunk中,所以之前A的预建产品可能已经改变了,那么这个时候,Vite必须强制刷新页面,让浏览器使用最新的预建产品。这仍然是一个二次预构建(所有依赖项重新打包+页面重新加载)过程。总体来说,2.9版本解决了pre-build阻塞服务启动的问题,但是没有彻底解决二次pre-build的问题。但在Vite3.0中,二次预构建的问题也得到了根本解决。那么Vite3.0是怎么做到的呢?核心解决方案是延迟处理,即延迟预构建行为,直到页面加载的最后阶段。此时Vite已经编译好了所有的源文件,可以准确记录所有需要预构建的依赖(包括Vite插件添加的一些依赖),然后统一预构建,响应预构建产品到浏览器。依赖于预构建的代码,在Vite中已经进行了多次重构。目前版本的实现比较复杂,后面会单独写一篇文章讨论实现细节。因此,与Vite2.0相比,Vite3.0在冷启动阶段主要有两个方面的优化:pre-build不再阻塞DevServer的启动,真正做到服务秒启动的效果。从根本上杜绝二次预建的发生。4.更新import.meta.glob语法。Vite3.0重写了import.meta.glob的实现,支持更灵活的glob语法,并增加了以下特性:多模式匹配:import.meta.glob(["./dir/*.js","./another/*.js"]);否定模式(!):import.meta.glob(["./dir/*.js","!**/bar.js"]);命名导入可以更好地进行TreeShaking:import.meta.glob("./dir/*.js",{import:"setup"});自定义查询参数:import.meta.glob("./dir/*.js",{query:{custom:"data"}});指定eager模式,替换原来的import.meta.globEager:import.meta.glob("./dir/*.js",{eager:true});三、量产阶段更新1、SSR产品默认使用ESM格式。在当前的社区生态中,很多SSR框架已经在使用ESM格式作为默认的Product格式。Vite3.0也积极拥抱社区,支持SSR构建默认以ESM格式打包的产品。2、RelativeBase支持Vite3.0官方支持RelativeBase(即configurebase:''),主要用于构建时无法确定基地址的场景。四、实验功能1、更细粒度的基础配置在某些场景下,我们需要将不同的资源部署到不同的CDN,比如将图片部署到单独的CDN,这就不同于JS/CSS部署服务来了。但是2.x版本只支持统一部署域名,即base配置。在3.0中,你可以使用renderBuiltUrl进行更细粒度的配置:'js'){return{runtime:`window.__toCdnUrl(${JSON.stringify(filename)})`}}else{return'https://cdn.domain.com/assets/'+filename}}}}目前该配置项不稳定,可能会在以后的小版本中修改。具体文档参见https://vitejs.dev/guide/build.html#advanced-base-options2。Esbuild是为生产环境预构建的。这应该是Vite架构的一个非常大的变化:原来只在开发阶段使用的依赖预构建的特性才能在生产中使用。在Vite2.x中,开发阶段使用Esbuild打包依赖,生产环境使用Rollup打包,使用@rollupjs/plugin-commonjs处理cjs依赖,会导致依赖处理不一致,导致部分生产构建错误。但在Vite3.0中,开发环境和生产环境都支持通过配置使用Esbulidpre-build,只需添加optimizeDeps.disabled:false的配置即可。不过,这个变化确实比较大。Vite团队并不打算将此作为v3的官方更新,而是默认不会启用的实验性功能。顺便说一句,Rollup将在未来几个月内发布v3的主要版本。要知道,距离Rollup2.0发布已经过去2年多了。这对于Rollup和Vite来说都是非常重要的事件。改变。由于Vite的架构严重依赖Rollup,在Rollupv3发布后,Vite也将发布Vite的第4个大版本。所以,Vite4.0的到来也不远了:)五、仓库的变化除了自身功能的演进,Vite仓库本身也发生了很多变化,从中我们也可以了解到社区的一些动向:不再支持Nodejs12,需要Node.js14.18+版本。单元测试和E2E测试完全从Jest迁移到Vitest。一方面,Vitest速度更快,体验更好。另一方面也可以在像Vite这样的大仓库中完善Vitest生态系统,进一步提高Vitest的稳定性。VitePress文档部分也参与了CI过程。包管理器pnpm迁移到v7。无论是Vite自带的包还是E2E测试的项目,在package.json中声明type:"module",即PureESM包,对外提供ESM格式的产品,推Pure的趋势ESM在社区中更进了一步。所有Vite官方插件均使用unbuild(新一代库构建工具)构建,pluin-vue-jsx和plugin-legacy迁移至TS。包大小优化。3.0进一步优化Vite自身产品和node_modules的体积,去除terser和node-forge的依赖,允许用户按需安装(node-forge的功能是实现https证书生成,可用@vitejs/plugin-basic-ssl插件替换),效果如下:PublishSizeInstallSizeVite2.9.144.38MB19.1MBVite3.0.03.05MB17.8MBReduction-30%-7%不得不说在其优化方面自己的包大小,Vite还是做的很细致,这也是很多库开发者忽略的一点。有时添加一个插件需要安装数百MB的依赖项,导致项目node_modules非常臃肿。这个时候你不妨了解一下Vite是如何优化自身尺寸的。六、未来规划首先,在Vite3.0发布后,我们将重点保障Vite3.0的稳定性,解决当前的一系列问题。其次,Rollup团队会在接下来的几个月发布一个新的大版本,Vite也会继续跟进,然后发布v4版本,将目前一些实用的功能稳定在v4版本中。总结Vite3.0带来了一些比较大的架构变化,比如依赖预构建重构,支持生产环境Esbuild预打包依赖,全面支持PureESM等。当然,这个版本集中也发布了一些比较小的breakchanges,比如importChangesto.meta.globsyntax等等。总之,在过去的一年多时间里,Vite团队进行了大量的功能改进和架构升级。目前GithubStar已经达到44k+,还在持续维护中。同时,Vite的社区生态也在逐步完善,如Vitest、VitePress、丰富的社区插件[10]、以及众多内置Vite的社区框架等。可以预见,Vite将持续发展一段未来很长一段时间。开发,不断迭代,提供更好的用户体验,成为下一代前端工具链。参考文献[1]Github地址:https://github.com/vitejs/awesome-vite。[2]vitejs.dev:vitejs.dev。[3]VitePress:https://vitepress.vuejs.org/。[4]维测:https://vitest.dev/。[5]vite-plugin-pwa:https://vite-plugin-pwa.netlify.app/。[6]VitePress:https://vitepress.vuejs.org/。[7]v2.vitejs.dev:https://v2.vitejs.dev/。[8]vite-plugin-optimize-persist:https://github.com/antfu/vite-plugin-optimize-persist。[9]公关:https://github.com/vitejs/vite/pull/6758。[10]社区插件:https://github.com/vitejs/awesome-vite。
