Vite 是如何站在巨人的肩膀上实现的

news2025/1/21 4:49:40

所谓的巨人,指的就是 Vite 底层所深度使用的两个构建引擎——Esbuild和Rollup。这两个构建引擎对于 Vite 来说究竟有多重要呢?在 Vite 的架构中,这两者各自扮演了什么样的角色?接下来,我们一起拆解 Vite 的双引擎架构,深入分析Esbuild和Rollup在 Vite 中的作用。

一、Vite 架构

很多人对 Vite 的双引擎架构仅仅停留在开发阶段使用 Esbuild,生产环境用 Rollup的阶段,殊不知,Vite 真正的架构远没有这么简单,下面是一张Vite的架构图。

image.png

对于 Vite 的双引擎架构,从图中可以略窥一二。

二、Esbuild

必须要承认的是,Esbuild的确是 Vite 高性能的得力助手。在很多关键的构建阶段让 Vite 获得了相当优异的性能,如果这些阶段使用传统的打包器/编译器来完成的话,开发体验要下降一大截。那么,Esbuild 到底在 Vite 的构建体系中发挥了哪些作用?

2.1 依赖预构建

首先,在开发阶段的依赖预构建阶段,Esbuild将提供Bundle 打包工具。

image.png

一般来说,node_modules 依赖的大小动辄几百 MB 甚至上 GB ,会远超项目源代码,相信大家都深有体会。如果这些依赖直接在 Vite 中使用,会出现一系列的问题,这些问题我们在依赖预构建的小节已经详细分析过,主要是 ESM 格式的兼容性问题和海量请求的问题,不再赘述。总而言之,对于第三方依赖,需要在应用启动前进行打包并且转换为 ESM 格式。

Vite 1.x 版本中使用 Rollup 来做这件事情,但 Esbuild 的性能实在是太恐怖了,Vite 2.x 果断采用 Esbuild 来完成第三方依赖的预构建,至于性能到底有多强,大家可以参照它与传统打包工具的性能对比图。

image.png

当然,Esbuild 作为打包工具也有一些缺点。

  • 不支持降级到 ES5 的代码。这意味着在低端浏览器代码会跑不起来。
  • 不支持 const enum 等语法。这意味着单独使用这些语法在 esbuild 中会直接抛错。
  • 不提供操作打包产物的接口,像 Rollup 中灵活处理打包产物的能力(如renderChunk钩子)在 Esbuild 当中完全没有。
  • 不支持自定义 Code Splitting 策略。传统的 Webpack 和 Rollup 都提供了自定义拆包策略的 API,而 Esbuild 并未提供,从而降级了拆包优化的灵活性。

尽管 Esbuild 作为一个社区新兴的明星项目,有如此多的局限性,但依然不妨碍 Vite 在开发阶段使用它成功启动项目并获得极致的性能提升,生产环境处于稳定性考虑当然是采用功能更加丰富、生态更加成熟的 Rollup 作为依赖打包工具了。

2.2 单文件编译

在依赖预构建阶段, Esbuild 作为 Bundler 的角色存在。而在 TS(X)/JS(X) 单文件编译上面,Vite 也使用 Esbuild 进行语法转译,也就是将 Esbuild 作为 Transformer 来用。大家可以在架构图中Vite Plugin Pipeline部分找到。

image.png
也就是说,Esbuild 转译 TS 或者 JSX 的能力通过 Vite 插件提供,这个 Vite 插件在开发环境和生产环境都会执行,因此,我们可以得出下面这个结论。

Vite 已经将 Esbuild 的 Transformer 能力用到了生产环境。尽管如此,对于低端浏览器场景,Vite 仍然可以做到语法和 Polyfill 安全。

这部分能力用来替换原先 Babel 或者 TSC 的功能,因为无论是 Babel 还是 TSC都有性能问题,大家对这两个工具普遍的认知都是: 慢,太慢了。

当 Vite 使用 Esbuild 做单文件编译之后,提升可以说相当大了,我们以一个巨大的、50 多 MB 的纯代码文件为例,来对比Esbuild、Babel、TSC 包括 SWC 的编译性能。

image.png

可以看到,虽然 Esbuild Transfomer 能带来巨大的性能提升,但其自身也有局限性,最大的局限性就在于 TS 中的类型检查问题。这是因为 Esbuild 并没有实现 TS 的类型系统,在编译 TS(或者 TSX) 文件时仅仅抹掉了类型相关的代码,暂时没有能力实现类型检查。

也因此,快速上手这一节,我让大家注意初始化工程的构建脚本,vite build之前会先执行tsc命令,也就是借助 TS 官方的编译器进行类型检查。

当然,要解决类型问题,我更推荐大家使用 TS 的编辑器插件。在开发阶段就能早早把问题暴露出来并解决,不至于等到项目要打包上线的时候。

2.3 代码压缩

Vite 从 2.6 版本开始,就默认使用 Esbuild 来进行生产环境的代码压缩,包括 JS 代码和 CSS 代码。下图演示了在生产环境中 Esbuild 压缩器通过插件的形式融入到了 Rollup 的打包流程中。

image.png

那为什么 Vite 要将 Esbuild 作为生产环境下默认的压缩工具呢?因为压缩效率实在太高了。传统的方式都是使用 Terser 这种 JS 开发的压缩器来实现,在 Webpack 或者 Rollup 中作为一个 Plugin 来完成代码打包后的压缩混淆的工作。但 Terser 其实很慢,主要有 2 个原因。

  • 压缩这项工作涉及大量 AST 操作,并且在传统的构建流程中,AST 在各个工具之间无法共享,比如 Terser 就无法与 Babel 共享同一个 AST,造成了很多重复解析的过程。
  • JS 本身属于解释性 + JIT(即时编译) 的语言,对于压缩这种 CPU 密集型的工作,其性能远远比不上 Golang 这种原生语言。

因此,Esbuild 这种从头到尾共享 AST 以及原生语言编写的 Minifier 在性能上能够甩开传统工具的好几十倍。举个例子,我们可以看下面这个实际大型库(echarts)的压缩性能测试项目:

image.png

压缩一个大小为3.2 MB的库,Terser 需要耗费8798 ms,而 Esbuild 仅仅需要361 ms,压缩效率较 Terser 提升了二三十倍,并且产物的体积几乎没有劣化,因此 Vite 果断将其内置为默认的压缩方案。

总的来说,Vite 将 Esbuild 作为自己的性能利器,将 Esbuild 各个垂直方向的能力(Bundler、Transformer、Minifier)利用的淋漓尽致,给 Vite 的高性能提供了有利的保证。

三、Rollup

Rollup 在 Vite 中的重要性一点也不亚于 Esbuild,它既是 Vite 用作生产环境打包的核心工具,也直接决定了 Vite 插件机制的设计。那么,Vite 到底基于 Rollup 做了哪些事情?

3.1 生产环境 Bundle

虽然 ESM 已经得到众多浏览器的原生支持,但生产环境做到完全no-bundle也不行,会有网络性能问题。为了在生产环境中也能取得优秀的产物性能,Vite 默认选择在生产环境中利用 Rollup 打包,并基于 Rollup 本身成熟的打包能力进行扩展和优化,主要包含 3 个方面:

  • CSS 代码分割。如果某个异步模块中引入了一些 CSS 代码,Vite 就会自动将这些 CSS 抽取出来生成单独的文件,提高线上产物的缓存复用率。
  • 自动预加载。Vite 会自动为入口 chunk 的依赖自动生成预加载标签 ,如:
<head>
  <!-- 省略其它内容 -->
  <!-- 入口 chunk -->
  <script type="module" crossorigin src="/assets/index.250e0340.js"></script>
  <!--  自动预加载入口 chunk 所依赖的 chunk-->
  <link rel="modulepreload" href="/assets/vendor.293dca09.js">
</head>

这种适当预加载的做法会让浏览器提前下载好资源,优化页面性能。

  • 异步 Chunk 加载优化。在异步引入的 Chunk 中,通常会有一些公用的模块,如现有两个异步引入的 Chunk: A 和 B,而且两者有一个公共依赖 C,如下图:

image.png

一般情况下,Rollup 打包之后,会先请求 A,然后浏览器在加载 A 的过程中才决定请求和加载 C,但 Vite 进行优化之后,请求 A 的同时会自动预加载 C,通过优化 Rollup 产物依赖加载方式节省了不必要的网络开销。

3.2 兼容插件机制

无论是开发阶段还是生产环境,Vite 都根植于 Rollup 的插件机制和生态,如下面的架构图所示。

image.png

在开发阶段,Vite 借鉴了 WMR 的思路,自己实现了一个 Plugin Container,用来模拟 Rollup 调度各个 Vite 插件的执行逻辑,而 Vite 的插件写法完全兼容 Rollup,因此在生产环境中将所有的 Vite 插件传入 Rollup 也没有问题。

反过来说,Rollup 插件却不一定能完全兼容 Vite。不过,目前仍然有不少 Rollup 插件可以直接复用到 Vite 中,你可以通过这个站点查看所有兼容 Vite 的 Rollup 插件: vite-rollup-plugins.patak.dev/ 。

狼叔在《以框架定位论前端的先进性》 提到现代前端框架的几大分类,Vite 属于人有我优的类型,因为类似的工具之前有 Snowpack,Vite 诞生之后补齐了作为一个 no-bundle 构建工具的 Dev Server 能力(如 HMR),确实比现有的工具能力更优。但更重要的是,Vite 在社区生态方面比 Snowpack 更占先天优势。

Snowpack 自研了一套插件机制,类似 Rollup 的 Hook 机制,可以看出借鉴了 Rollup 的插件机制,但并不能兼容任何现有的打包工具。如果需要打包,只能调用其它打包工具的 API,自身不提供打包能力。

而 Vite 的做法是从头到尾根植于的 Rollup 的生态,设计了和 Rollup 非常吻合的插件机制,而 Rollup 作为一个非常成熟的打包方案,从诞生至今已经迭代了六年多的时间,npm 年下载量达到上亿次,产物质量和稳定性都经历过大规模的验证。某种程度上说,这种根植于已有成熟工具的思路也能打消或者降低用户内心的疑虑,更有利于工具的推广和发展。

四、总结

首先,Esbuild 作为构建的性能利器,Vite 利用其 Bundler 的功能进行依赖预构建,用其 Transformer 的能力进行 TS 和 JSX 文件的转译,也用到它的压缩能力进行 JS 和 CSS 代码的压缩。

接着,我给你介绍了 Vite 和 Rollup 的关系。在 Vite 当中,无论是插件机制、还是底层的打包手段,都基于 Rollup 来实现,可以说 Vite 是对于 Rollup 一种场景化的深度扩展,将 Rollup 从传统的 JS 库打包场景扩展至完整 Web 应用打包,然后结合开发阶段 no-bundle 的核心竞争力,打造出了自己独具一格的技术品牌。

因此,你可以看出双引擎对于 Vite 的重要性,如果要深入学习和应用 Vite,那么掌握 Esbuild 和 Rollup 的基础使用和插件开发是非常有必要的。在后面的讲解中,我们将一起进入双引擎本身的学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/522721.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

瑞吉外卖项目笔记02——员工管理、设置公共字段自动填充

三、员工信息管理 3.1 添加员工 注意&#xff1a;在设计数据库表字段时&#xff0c;给userName添加了唯一索引&#xff08;所以员工用户名是无法重复的&#xff09; 流程&#xff1a; 前端页面发送POST请求&#xff0c;后端接收到请求和数据&#xff0c;将用户数据添加到数…

PyQt5桌面应用开发(14):数据库+ModelView+QCharts

本文目录 PyQt5桌面应用系列下一个玩具报表一&#xff1a;Markdown文档的列表显示Widget的树、表、列报表Qt中的MVC框架模型视图代理 报表二&#xff1a;Markdown文档的长度图形数据&#xff1a;Markdown文档和Sqlite数据库代码总结 PyQt5桌面应用系列 PyQt5桌面应用开发&#…

FE_Vue学习笔记 框架的执行流程详解

1 分析脚手架结构 &#xff08;1&#xff09;CLI就是 command line interface 的缩写。Vue CLI官网&#xff1a;Vue CLI &#xff08;2&#xff09;安装过程&#xff1a; &#xff08;PS&#xff1a; 提前安装过node.js了&#xff0c;没有安装的可以打开这个&#xff1a;Downl…

Flutter - 搭建引擎调试环境(iOS)

文章目录 前言开发环境安装依赖环境1. python32. depot_tools 获取引擎项目1. 创建engine空目录2. 创建.gclient文件并配置3. 指定引擎版本4. 同步引擎源码 搭建调试环境1. 构建编译1.1 生成构建所需文件1.1.1 主机端1.1.2 iOS端 1.2 完成构建编译 2. Xcode调试2.1 设置本地引擎…

Qt扫盲-QAreaSeries理论总结

QAreaSeries理论总结 一、概述二、使用1. 创建QAreaSeries 对象2. 填充数据3. 设置区域4. 将面积图 与绘图设备关联5. 将绘图设备与 GUI窗口关联 一、概述 QAreaSeries类以面积图的形式显示数据。QAreaSeries用于显示定量数据。它基于QLineSeries 类&#xff0c;边界线条之间的…

二、Docker在Linux下的安装

文章目录 Docker在Linux下的安装Docker安装的前提环境安装1. 卸载旧的版本2. 安装yum-utils包3. 设置镜像的仓库4. 安装Docker相关的内容 启动Docker运行hello-world查看下载的hello-world镜像 了解卸载Docker Docker在Linux下的安装 Docker安装的前提环境 CentOS Docker 安装…

【LLM系列之PaLM】PaLM: Scaling Language Modeling with Pathways

论文题目&#xff1a;《Scaling Instruction-Finetuned Language Models》 论文链接&#xff1a;https://arxiv.org/abs/2204.02311 github链接1&#xff1a;https://github.com/lucidrains/PaLM-pytorch/tree/main; github链接2:https://github.com/conceptofmind/PaLM huggin…

每天一个提高效率的Matlab编程小技巧(1)-dbstop if error

相信在matlab调试程序的时候都遇到过这种情况&#xff1a;运行程序时命令行报错&#xff0c;而且出错的位置在我们自己定义的函数里&#xff0c;比如下面这个例子&#xff1a; 主函数main.m: a[1 2 3]; b[4 5]; csum_squares(a,b); 子函数sum_squares.m function csum_squa…

AI孙燕姿 ?AI东雪莲 !—— 本地部署DDSP-SVC一键包,智能音频切片,本地训练,模型推理,为你喜欢的角色训练AI语音模型小教程

目录 感谢B站UP羽毛布团 演示视频 稻香——东雪莲 虚拟——东雪莲 反方向的钟——东雪莲 晴天龙卷风——东雪莲 DDSP-SVC 3.0 (D3SP) 是什么&#xff1f; 下载资源&#xff1a; 解压整合包 准备数据集 智能音频切片 数据集准备 填写训练设置和超参数 开始训练 推…

这个抓包工具太强了,科来网络分析系统强烈推荐

一直以来抓包工具&#xff0c;都推荐和使用wireshark&#xff0c;简单好用。最近发现一款更强大好用的网络分析工具&#xff0c;科来网络分析系统。且技术交流版是完全免费的&#xff0c;无需注册激活。这里强烈推荐和分享给大家。这可是个网络报文分析和监控神器。有多强大&am…

【CSS系列】第七章 · CSS盒子模型,看这一篇就够了

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

Protobuf-net3.2.8中的protogen.exe之使用

目录 protobuf是个好东西 遇到问题 顺便研究一下命令行程序如何调试 protobuf是个好东西 protobuf是一个轻量级的数据格式&#xff0c;相比json&#xff0c;它的数据量为json的1/3&#xff0c;且存储方式为2进制&#xff0c;并进行了压缩&#xff0c;序列化和反序列化更快&…

效率与性能并存——离不开 Visual Studio Code 的前端开发与我

文章目录 &#x1f4cb;前言&#x1f3af;题外话&#xff1a;我与 VSCode 的那些事&#x1f3af;VSCode 的强大之处&#x1f9e9;VSCode 的诞生&#x1f9e9;VSCode 的一些功能 &#x1f3af;优与劣&#xff08;简单小结&#xff09;&#x1f4dd;最后 &#x1f4cb;前言 许久…

JVM 原理简介

JVM一直是java知识里面进阶阶段的重要部分&#xff0c;如果希望在java领域研究的更深入&#xff0c;则JVM则是如论如何也避开不了的话题&#xff0c;本系列试图通过简洁易读的方式&#xff0c;讲解JVM必要的知识点。 运行流程 我们都知道java一直宣传的口号是&#xff1a;一次编…

股票K线基础知识1

K线图 K线图是反映价格在某一时间周期内波动情况的图表&#xff0c;它由开盘价、收盘价、最高价、最低价四个要素构成&#xff0c;若当日收盘价高于开盘价&#xff0c;这表明价格处于上涨状态&#xff0c;此时K线图多用红色表示&#xff1b;若当日收盘价低于开盘价&#xff0c…

(转载)从0开始学matlab(第1天)—变量和数组

MATLAB 程序的基本数据单元是数组。一个数组是以行和列组织起来的数据集合&#xff0c;并且拥有一个数组名。数组中的单个数据是可以被访问的&#xff0c;访问的方法是数组名后带一个括号&#xff0c;括号内是这个数据所对应行标和列标。标量在 MATLAB 中也被当作数组来处理——…

JavaScript实现输入文字,指定输出遍数的代码

以下为实现输入文字&#xff0c;指定输出遍数的程序代码和运行截图 目录 前言 一、实现输入文字&#xff0c;指定输出遍数 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.…

Prometheus+Alertmanager+webhook-dingtalk实现钉钉告警

文章目录 一、前提准备及规划二、安装及启动2.1 Prometheus安装启动2.2 Node_export安装启动2.3 Alertmanager安装启动2.4 Webhook-dingtalk安装启动 三、配置及测试3.1 Webhook-dingtalk配置钉钉webhook地址3.2 Alertmanager配置钉钉告警3.3 Prometheus集成Alertmanager及告警…

基于Docker的深度学习环境部署以及WSL和linux镜像问题

基于Docker的深度学习环境部署 1. 什么是Docker&#xff1f;2. 深度学习环境的基本要求3. Docker的基本操作3.1 在Windows上安装Docker3.2 在Ubuntu上安装Docker3.3 拉取一个pytorch的镜像3.4 部署自己的项目3.5 导出配置好项目的新镜像 4. 分享新镜像4.1 将镜像导出为tar分享给…

android应用的一种图标隐藏

在Android10之前&#xff0c;应用程序通过调用PackageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)函数来实现图标隐藏。 但是在android10之后&#xff0c;所有具有四大组件和需要申请…