认识
一、认识
Rspack 是一个基于 Rust
编写的高性能 JavaScript
打包工具, 它提供对 Webpack
生态良好的兼容性,能够无缝替换 Webpack
, 并提供闪电般的构建速度。
二、特点
三、问题
3.1 RsPack Vs Vite?
Vite
提供了很好的开发者体验,但 Vite
在生产构建中依赖了 Rollup
,因此与其他基于 JavaScript
的工具链一样,面临着生产环境的构建性能问题。
另外,Rollup
相较于 Webpack
做了一些平衡取舍,在这里同样适用。比如,Rollup
缺失了 Webpack
对于拆包的灵活性,即缺失了 optimization.splitChunks
提供的很多功能。
3.2 RsPack Vs Rollup?
Rspack
和 Rollup
虽然都是打包工具,但是两者的侧重领域不同,Rollup
更适合用于打包库,而 Rspack
适合用于打包应用。因此 Rspack
对打包应用进行了很多优化,如 HMR
、Bundle splitting
等功能,而 Rollup
则比 Rspack
的编译产物对库更为友好,如更好的 ESM
产物支持。 社区上也有很多的工具在 Rollup
基础上进行了一定的封装,提供了对应用打包更友好的支持,如 vite
和 wmr
, 目前 Rspack
比 Rollup
有更好的生产环境构建性能。
3.3 RsPack Vs Esbuild?
我们在内部进行过大规模地将 esbuild
作为通用的 Web App
构建工具的实践,但是遇到如下一些问题:
-
缺乏对
JavaScript HMR
和增量编译的良好支持,这导致大型项目中的HMR
性能较差。 -
拆包策略也非常原始,难以满足业务复杂多变的拆包优化需求。
3.4 将 Webpack 升级为 RsPack 之后会有什么问题吗?
问题一、相同路径、相同名称 Chunk
处理方式不同: 将 Webpack
升级为 RsPack
之后, 针对相同路径、相同名称的 Chunk
处理方式不一样。Webpack
遇到相同的 Chunk
会覆盖之前的。RsPack
遇到相同的 Chunk
, 会直接使用已经加载的 Chunk
。这样会导致之前在 Webpack
构建下, 一个页面加载了一份正常的 Chunk
, 基于 MicroApp
恰巧加载了一份相同路径、名称的 Chunk
, 但是他们的构建方式是不一样的、逻辑也不一样。由于 Webpack
后面的 Chunk
会覆盖前面的 Chunk
, 所有 Webpack
不会又问题。但是在 RsPack
构建下, 本来应该使用的是后面的 Chunk
, 但是因为 Chunk
的复用逻辑, 导致 MicroApp
使用的是前面的 Chunk
, 导致 MicroApp
渲染异常。以下是 RsPack Chunk
处理逻辑:
var installedChunks = {"platform": 0,};
__webpack_require__.O.j = function(chunkId) { return installedChunks[chunkId] === 0; };
// install a JSONP callback for chunk loading
var webpackJsonpCallback = function(parentChunkLoadingFunction, data) {
var chunkIds = data[0];
var moreModules = data[1];
var runtime = data[2];
// add "moreModules" to the modules object,
// then flag all "chunkIds" as loaded and fire callback
var moduleId, chunkId, i = 0;
if (chunkIds.some(function(id) { return installedChunks[id] !== 0; })) {
for (moduleId in moreModules) {
if (__webpack_require__.o(moreModules, moduleId)) {
__webpack_require__.m[moduleId] = moreModules[moduleId];
}
}
if (runtime) var result = runtime(__webpack_require__);
}
if (parentChunkLoadingFunction) parentChunkLoadingFunction(data);
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (
__webpack_require__.o(installedChunks, chunkId) &&
installedChunks[chunkId]
) {
installedChunks[chunkId][0]();
}
installedChunks[chunkId] = 0;
}
return __webpack_require__.O(result);
};
问题二、EsModule
语法中不允许有 CommonJS
导出: 我们在之前的 Tsx
中, 有过一些不正确的模块语法。比如上面通过 import
导入模块, 下面通过 module.exports
导出。EsModule
和 CommonJS
混合在一起, 这在 Webpack
中没有问题, 但是 RsPack
会严格校验。
3.5 Vite、Rollup、Webpack、RsPack 各自优势、特点?
Vite
: 采用双引擎架构, 开发阶段使用 Esbuild
+ no-bundle
服务,生产环境用 Rollup
编译构建。Vite
在开发阶段, Vite
项目的启动可以分为两步。第一步是 依赖预构建, 借助 Esbuild
超快的编译速度来将不同规范的代码转换为 ES Module
格式, 方便浏览器直接加载, 此外, 预构建还会将第三方库分散的文件合并打包到一起, 减少 HTTP
请求数量; 第二步是 Dev Server
的启动, 基于浏览器原生 ESModule
的支持实现了 no-bundle
服务,实现开发阶段的 Dev Server
, 进行模块的按需加载, 可以直接在浏览器中运行源码, 无需事先打包。当浏览器请求一个模块时, Vite
会即时地编译和执行对应的代码, 会经历一系列的编译流程, 然后 Vite
会将编译结果响应给浏览器。Vite
生产环境借助 Rollup
, 从 AST
解析的功能开始, 完成代码的词法分析(tokenize
)和语义分析(parse
), 实现模块依赖图和作用域链的搭建, 并完成 Tree Shaking
、循环依赖检测及 Bundle
代码生成。 在 HMR
方面, Vite
的热更新则只会针对改动的模块进行更新,提高了更新速度。当开发者修改了一个模块的代码, Vite
可以在几毫秒内完成热更新, 将更新后的模块发送到浏览器中, 让开发者能够更快地看到代码修改后的效果。
Rollup
是一款基于 ES Module
模块规范实现的 JavaScript
打包工具。通过静态分析构建模块依赖图, 并进行 Tree Shaking
, 剔除未使用的代码。Rollup
可以直接处理 Es Modules
, 对于 CommonJs
需要通过插件来转换。Rollup
本身不提供开发服务器、HMR
等功能,主要定位于生产构建。
Webpack
: 无论开发环境还是生产环境, Webpack
会扫描整个项目的所有文件, 将所有模块进行静态分析,
分析它们之间的依赖关系, 形成依赖树, 然后一次性编译生成文件, 生成最终代码前, 根据模块中出现的特性依赖, 补充相应运行时代码, 比如立即表达式 IIFE
、Webpack
runtime
运行时代码, 生成最终产物。Webpack
实现了一套自己的 CommonJS
规范, 在 Webpack
中, 每个模块都被包装在一个函数中, 这个函数接受一个对象, 这个对象有 exports
、require
、module
等属性, 通过这种方式实现了模块的隔离, 每个模块都有自己的作用域, 不会污染全局作用域。在 HMR
方面, Webpack
的热更新需要整个模块链重新打包和替换,对于大型项目可能会有延迟。在热更新过程中, Webpack
会检测到模块的变化,然后重新编译整个模块链,最后将更新后的模块替换到浏览器中。这个过程相对复杂,可能会导致一定的延迟。另外, Webpack 5.x
内置了更加完善、更智能、更强大的 TreeShaking
、代码分割、作用域提升 等。Webpack 5.x
新特性: 1. 模块联邦(Module Federation
), 模块联邦是 Webpack 5
中最引人注目的新特性之一, 它允许不同独立构建的应用在运行时共享彼此的模块,而无需事先打包到同一个 bundle
里; 2. 持久化缓存(Persistent Caching
), Webpack 5
引入了持久化缓存功能,将中间构建结果保存到磁盘上,这样在二次构建时可以复用已有结果; 3. 更高效的 Tree Shaking
与代码优化: 使生成的产物更精简,提升浏览器缓存效果; 4. 删除 Node.js
核心模块的自动 Polyfill
, 以前 Webpack
会自动为浏览器端补充 Node.js
核心模块(例如 Buffer
、process
、crypto
等)的 polyfill
, Webpack 5
默认不再提供这些 polyfill
, 要求开发者根据项目需要手动添加或调整代码逻辑; 5. 内置资源模块(Asset Modules
), 在 Webpack 4
及之前版本中,处理静态资源(如图片、字体等)通常依赖于第三方 Loader
(如 file-loader
、url-loader
、raw-loader
等), Webpack 5
内置了资源模块,开发者可以直接在代码中 import
资源, 无需额外安装 Loader
。
RsPack
是一个基于 Rust
编写的高性能 JavaScript
打包工具, 它提供对 Webpack
生态良好的兼容性,能够无缝替换 Webpack
, 并提供闪电般的构建速度。1. Rust
语言优势: Rspack
使用 Rust
语言编写, 得益于 Rust
的高性能编译器支持, Rust
编译生成的 Native Code
通常比 JavaScript
性能更为高效; 2. 高度并行的架构: Webpack
受限于 JavaScript
对多线程的羸弱支持,导致其很难进行高度的并行化计算,而得益于 Rust
语言的并行化的良好支持, Rspack
采用了高度并行化的架构,如模块图生成,代码生成等阶段,都是采用多线程并行执行,这使得其编译性能随着 CPU
核心数的增长而增长,充分挖掘 CPU
的多核优势; 3. 内置大部分的功能: 事实上 Webpack
本身的性能足够高效,但是因为 Webpack
本身内置了较少的功能,这使得我们在使用 Webpack
做现代 Web App
开发时,通常需要配合很多的 plugin
和 loader
进行使用,而这些 loader
和 plugin
往往是性能的瓶颈,而 Rspack
虽然支持 loader
和 plugin
,但是保证绝大部分常用功能都内置在 Rspack
内,从而减小 JS plugin | loader
导致的低性能和通信开销问题; 4. 增量编译: 尽管 Rspack
的全量编译足够高效,但是当项目庞大时,全量的编译仍然难以满足 HMR
的性能要求,因此在 HMR
阶段,我们采用的是更为高效的增量编译策略,从而保证,无论你的项目多大,其 HMR
的开销总是控制在合理范围内。但是, RsPack
与 Webpack
对构建产物体积的优化方面, TreeShaking
、代码分割、作用域提升 做的不是那么完善。