TSX车型的智能科技配置怎么样?,

Vite 4.3发布!15+优化策略带来1.5x ~2x 性能提升!

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

高级前端进阶

今天给大家带来的主题是 Vite 4.3版本的发布,总体性能提升大约 1.5 ~2倍,同时带着大家一起来聊聊 Vite 4.3的 10 大优化手段。深入后面内容阅读之前非常建议大家提前阅读相关专题,以下是已发表文章的传送门:

话不多说,直接开始。

1.Vite 生态的四大金刚

1.1 ESM

ESM 代表 EMACScript 模块。 它是 JavaScript 语言规范的最新补充,用于处理如何在 Javascript 应用程序中加载模块。在 ES6 之前,JS 不支持模块化,对于大型项目的开发很不友好,所以社区出现了 CommonJS 、 AMD 、CMD、UMD等不同的模块规范,但在 ES6 中引入 ESM 之后,JS 从此有了自己的模块体系,CommonJS/CMD/AMD/UMD 也不再需要。

值得注意的是,Chrome >61、Safari > 11、Firefox > 60、Edge > 16、Opera > 48 等主流浏览器都已经原生支持 ESM,ESM在短短的几年经历了非常快速的发展。

1.2 Rollup

Rollup 是 JavaScript 的模块打包器,它可以将小块代码编译成更大更复杂的东西,例如库或应用程序。

Rollup 使用标准化的 ES 模块来编写代码,而不是 CommonJS 和 AMD 等特殊模块解决方案,因为 ES 模块让开发者可以自由、无缝地组合最喜欢的库中最有用的函数。 而 Rollup 负责优化 ES 模块以在现代浏览器中更快地进行本机加载。

对于支持 ES 模块的环境,Rollup 可以输出优化的 ES 模块; 对于不需要 ES 模块的环境,Rollup 可以将代码编译为其他格式,例如 CommonJS 、AMD 和 IIFE 风格的脚本,从而使得最大程度上面向未来编码。

Rollup具有以下明显优势:

  • 支持多种输出格式:ES 模块、CommonJS、UMD、SystemJS 等, 打包产物不仅适用于Web,也适用于许多其他平台,比如Node。
  • Tree-shaking,也称为“Live Code Inclusion”,是 Rollup 消除给定项目未使用的代码的过程。 它是一种消除死代码的形式,但在输出大小方面比其他方法更有效。
  • 仅使用输出格式的导入机制而不是客户加载程序代码,根据不同的入口点和动态导入拆分代码
  • Rollup 有一个易于学习的插件 API,允许开发者使用很少的代码实现强大的代码注入和转换, 已经被 Vite 和 WMR 采用。

1.3 Esbuild

esbuild 是一个用 Go 语言编写的用于打包,压缩 Javascript 代码的工具库。它最突出的特点就是打包速度极快 (extremely fast),下图是 esbuild 跟 webpack, rollup, Parcel 等打包工具打包效率的一个 benchmark。

esbuild具有以下明显的优势:

  • 用 Go 编写并编译为本机代码,而Go 的设计核心是并行性
  • 内部的算法经过精心设计,使得所有可用的 CPU 内核完全饱和。
  • 自己编写所有内容而不是使用三方库,从而带来了很多性能优势。
  • 最大限度重用AST、存储内存等

1.4 SWC

SWC(Speedy Web Compiler) 是一个可扩展的基于 Rust 的平台,用于下一代快速开发工具。 Next.js、Parcel 和 Deno 等工具以及 Vercel、字节跳动、腾讯、Shopify 等公司都在使用 SWC。

在 Github 上,SWC 已经有超过 26.3k 的 star 和 1k 的 fork,每周的平均下载量达到了 1983k。目前有超过 6.2k 的项目使用 SWC、项目贡献人数也达到了 200+,增长势头非常迅猛。

SWC:

SWC 可用于编译和打包工作,是一个超快速的 JavaScript 编译器。 对于编译,SWC 读取 JavaScript / TypeScript 文件,并输出所有主流浏览器都支持的代码。在性能上,SWC 比其他打包方案具有明显的优势。

在性能优势上,因为有Rust的加持,SWC官方宣称: 在单线程上比 Babel 快 20 倍,在四核上快 70 倍。

同时,SWC 还被设计为可扩展的。目前,支持以下核心能力,更多能力也在不断增强:

  • 代码编译(Compilation)
  • 打包:swcpack,开发中
  • 代码压缩
  • 使用 WebAssembly进行转换
  • Webpack集成:使用swc-loader
  • 提高Jest性能:使用 @swc/jest
  • 自定义插件

Vite 4.x 开始借助了 SWC 的诸多能力,特别是在 React 项目中,可以利用@vitejs/plugin-react、@vitejs/plugin-react-swc (new)插件。这也是为什么下面性能对比的图中有babel、swc两种方案的原因。

上面介绍了Vite生态的几个主角,接下来大家一起来看看什么是Vite。

2.什么是 Vite

Vite 是由 Vue.js 的创建者 Evan You 推出的下一代前端构建工具。 它是官方 Vue CLI 的替代品,速度非常快。Vite利用原生 ESM 并使用 Rollup 处理开发和打包工作。 从功能上讲,它的工作方式类似于预配置的 webpack 和 webpack-dev-server,但在速度方面具有无可比拟的优势。

Vite 具有以下明显特征:

  • Vite 提供了一个 HMR API, 在应用程序运行时交换、添加或删除模块,无需完全重新加载。 显著加快开发过程,因为在对应用程序进行更改时会保留应用程序状态。 如果对任何文件进行更改,会注意到更改的速度比普通 Vue 或 React 应用程序快得多。
  • Vite 支持开箱即用的 .ts 文件, 它使用名为 esbuild 的极其快速的 JavaScript 打包器将 TypeScript 代码转换为 JavaScript。
  • Vite 在提供的所有 JavaScript 文件中检测类裸模块导入,并将它们重写为解析路径以反映包在 node_modules 文件夹中的位置,以便浏览器可以正确处理它们。
  • Vite 开箱即用地支持 Vue 3 单文件组件 、Vue 3 JSX 组件和 Vue 2 组件。 它还支持 JSX 文件(.jsx、.tsx)、CSS 文件导入、PostCSS、CSS 模块和 CSS 预处理器,如 Sass、LESS 和 Stylus。

3. Vite 4.3 新特性

3.1 性能改进明显

在这个 minor 发布中,Vite 专注于提高开发服务器的性能。 解析逻辑得到简化,改进了热更新路径并实现了更智能的缓存以查找 package.json、TS 配置文件和解析 URL。

与 Vite 4.2 相比,Vite 4.3 带来了全面的速度提升。

下面是测量的性能改进,它测试了具有 1000 个 React 组件的应用冷热启动场景下,开发服务器的时间开销,以及根组件和叶组件的 HMR 时间。

Vite 4.3的启动时间

Vite 4.3的热更新时间

此性能运行的规格和版本如下:

  • CPU:Ryzen 9 5900X,内存:DDR4-3600 32GB,SSD:WD Blue SN550 NVME SSD
  • Windows 10 专业版 21H2 19044.2846
  • Node.js 18.16.0
  • Vite 和 React 插件版本
  • Vite 4.2 (babel): Vite 4.2.1 + plugin-react 3.1.0
  • Vite 4.3 (babel): Vite 4.3.0 + plugin-react 4.0.0-beta.1
  • Vite 4.2 (swc): Vite 4.2.1 + plugin-react-swc 3.2.0
  • Vite 4.3 (swc): Vite 4.3.0 + plugin-react-swc 3.3.0

总体来看,早期报告表明,在测试 Vite 4.3 测试版时,实际应用程序的开发启动时间提高了 1.5 ~ 2 倍,接下来一起看看Vite做了哪些性能优化策略。

3.2 详述15大性能改进手段

更智能的 resolve 策略

Vite 解析所有接收到的 URL 和路径以获取目标模块。

在 Vite 4.2 中,有很多冗余的 resolve 逻辑和不必要的模块搜索。 Vite 4.3 使 resolve 逻辑更简单、更严格、更准确,以减少计算和 fs 调用。

更简单的解决方案

Vite 4.2严重依赖 resolve 包来解析依赖的 package.json,查看 resolve 的源码发现解析 package.json 时有很多无用的逻辑。

Vite 4.3 摒弃了此 resolve,遵循更简单的 resolve 逻辑:直接检查嵌套父目录中是否存在 package.json。

更严格的 resolve

Vite 必须调用 Nodejs fs API 来查找模块, 但是 IO 很昂贵。

Vite 4.3 缩小了文件搜索范围,并跳过搜索一些特殊路径,以尽可能减少 fs 调用。 例如:

  • 由于 # 符号不会出现在 URL 中,用户可以控制源文件路径中没有 # 符号,因此 Vite 4.3 不再检查用户源文件中带有 # 符号的路径,而是仅在 node_modules 中搜索。
  • 在Unix系统中,Vite 4.2会先检查根目录下的每一个绝对路径,对大多数路径都可以,但是如果绝对路径以根开头就很容易失败。 为了在 /root/root 不存在的情况下跳过搜索 /root/root/path-to-file,Vite 4.3 会在开头判断 /root/root 作为目录是否存在,并预先缓存结果。
  • 当 Vite 服务器收到@fs/xxx 和@vite/xxx 时,就不需要再解析这些 URL。 Vite 4.3 直接返回之前缓存的结果,不再重新解析。

更准确的resolve

Vite 4.2 在文件路径为目录时递归解析模块,会导致不必要的重复计算。 Vite 4.3 将递归解析扁平化,并对不同类型的路径应用适当的解析,展平后缓存一些 fs 调用也更容易。

使用绝对文件路径缓存

Vite 4.3 打破了解析 node_modules 包数据的性能瓶颈。

Vite 4.3 不仅使用了绝对路径(/root/node_modules/pkg/foo/bar.js & /root/node_modules/pkg/foo/baz.js),还使用了遍历目录(/root/node_modules/pkg/foo & /root/node_modules/pkg) 作为 pkg 缓存的键。

另一种情况是,Vite 4.2 在单个函数中查找深层导入路径的 package.json。例如 Vite 4.2 解析 a/b/c/d 等文件路径时,首先检查根 a/package.json 是否存在, 如果没有,则按照a/b/c/package.json -> a/b/package.json的顺序查找最近的package.json,但事实是查找根package.json和最近的package.json应该处理分开,因为在不同的解析上下文中需要它们。 Vite 4.3 将根 package.json 和最近的 package.json 解析分成两部分,这样它们就不会混在一起。

修复 fs.realpathSync 问题

Nodejs 中有一个有趣的 realpathSync 问题,它指出 fs.realpathSync 比 fs.realpathSync.native 慢 70 倍。

export function getRealPath(path: string) {    try {        return realpathSync.native(path)    } catch (e) {        if (e.code === "ENOENT") {            return null        }        throw e    }}

但 Vite 4.2 仅在非 Windows 系统上使用 fs.realpathSync.native,因为它在 Windows 上的行为不同。 为了解决这个问题,Vite 4.3 在 Windows 上调用 fs.realpathSync.native 时添加了网络驱动器验证(network drive validation)。

HMR 防抖

考虑两个简单的依赖链 C <- B <- A & D <- B <- A,当 A 被编辑时,HMR 将从 A 传播到 C 和 A 传播到 D。这导致 A 和 B 在 Vite 中被更新两次。

Vite 4.3 缓存了这些遍历的模块,避免多次搜索。 这可能会对那些带有组件桶导入的文件结构产生很大的影响。 它也适用于由 git checkout 触发的 HMR。

并行化

并行化始终是获得更好性能的好选择。 在 Vite 4.3 中并行化了一些核心功能,包括:导入分析、提取 deps 的导出、解析模块 url 和运行批量优化器。

并行化后 Vite 4.3 确实有显著的性能改进。

非阻塞 tsconfig 解析

Vite 服务器在预绑定 ts 或 tsx 时需要 tsconfig 数据。

Vite 4.2 在服务端启动之前,需要在插件钩子 configResolved 中等待 tsconfig 数据解析完成。 而 Vite 4.3 会在服务器启动前初始化 tsconfig 解析,但服务器不会等待。 解析过程在后台运行。 但是,一旦有ts相关的请求进来,就得等tsconfig解析完了。

非阻塞文件处理

Vite 中有大量的 fs 调用,其中一些是同步的, 这些同步 fs 调用可能会阻塞主线程,Vite 4.3 将它们改为异步。

import { unlinkSync } from 'node:fs';try {  unlinkSync('/tmp/hello');  // 注意:有sync标记的都是同步的方法  console.log('successfully deleted /tmp/hello');} catch (err) {  // handle the error}


此外,并行化异步函数也更容易,关于异步函数需要关心的一件事是,可能有许多 Promise 对象在解析后要释放。 由于更智能的解析策略,释放 fs-Promise 对象的成本要低得多。

用回调替换 *yield

Vite 使用 tsconfck(@dominikg)来查找和解析 tsconfig 文件。 tsconfck 过去常常通过 *yield 遍历目标目录,生成器的一个缺点是它需要更多的内存空间来存储它的生成器对象,并且在运行时会有大量的生成器上下文切换。

所以Vite目前 开始在核心中用回调替换 *yield。

将 startsWith 和 endsWith 替换为 ===

Vite 4.2 使用 startsWith 和 endsWith 来检查热门 URL 中的标题和尾随 '/'。 通过 str.startsWith('x') 和 str<0> === 'x' 的执行基准,发现 === 比 startsWith 快大约 20%。

同时,endsWith 比 === 慢大约 60%。

避免重新创建正则表达式

Vite 需要大量的正则表达式来匹配字符串,大部分都是静态的,所以只用它们的单例会好很多。Vite 4.3 提升了正则表达式,以便可以被重用。

放弃生成自定义错误

Vite 4.2 中有一些自定义错误,以实现更好的 DX(开发体验)。 这些错误可能会导致额外的计算和垃圾收集,从而降低 Vite 的速度。

在 Vite 4.3 中,放弃生成一些自定义错误(例如 package.json NOT_FOUND 错误)并直接抛出原始错误以获得更好的性能。

4.本文总结

本文主要和大家探讨 Vite 4.3 版本的发布,总体性能提升大约 1.5 ~2倍,同时带着大家一起来聊了 Vite 4.3的 10 大优化手段。因为篇幅有限,文章并没有过多展开,如果有兴趣,文末的参考资料提供了优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!


参考资料

https://vitejs.dev/blog/announcing-vite4-3.html

https://www.educative.io/answers/what-is-vitejs

https://sun0day.github.io/blog/vite/why-vite4_3-is-faster.html#fs-realpathsync-issue

https://vitejs.dev/blog/announcing-vite4.html

2024-05-10

后面没有了,返回>>电动车百科