Parcel 2 RC
Parcel 团队非常高兴地宣布第一个 Parcel 2 发布候选版本!这个版本包括许多改进,包括新功能、更好的性能、API 一致性以及大量的 bug 修复和稳定性改进。
通过原生 ES 模块的自动差异化打包
#Parcel 2 现在自动为现代浏览器生成原生 ES 模块,并为旧浏览器生成回退的经典脚本。这通过提供现代语法而不是转译到 ES5,显著减小了大多数用户的包大小。
经典脚本 vs ES 模块
#Parcel 的一个基本设计目标一直是像 Web 一样工作。我们尽量避免锁定,尽可能遵循 Web 标准,不发明 Parcel 特定的语法或功能。在 Parcel v1 中,这一点在处理 HTML 中的 <script>
标签时并不成立。之前,Parcel 会将所有脚本视为模块,包括支持 import
和 export
语句,并将每个文件的顶级作用域与其他模块隔离。虽然方便,但这实际上不是浏览器中 <script>
标签的工作方式。
没有 type="module"
的经典脚本实际上将顶级作用域中的变量视为全局变量,并且不能有导入或导出,所以现在 Parcel 也匹配这种行为。这还修复了一些像 jQuery 这样的传统库的问题,这些库期望在全局脚本环境中运行,而不是在隔离的模块中。
当你从 Parcel 的先前版本升级时,你需要在脚本标签中添加 type="module"
属性。你将看到如下诊断,指出你需要修改的位置。
差异化打包
#差异化打包的思想是为不同目标生成代码的多个版本,并允许浏览器选择最优的版本下载。现代浏览器支持 <script type="module">
,除了支持导入和导出语法外,还支持其他现代语法,如类、箭头函数、async/await 等。提供现代语法而不是将此语法转译为旧的 JavaScript 版本,可以显著减少包大小并改善加载时间。但是,如果你仍需支持旧浏览器,可以使用 <script nomodule>
发送代码的转译版本,该版本仅在旧浏览器中加载。这给你带来了两全其美 - 为大多数用户提供更小的包,同时不牺牲旧浏览器上的用户。
使用模块/非模块模式的差异化打包现在在 Parcel 2 中完全自动化。只需在 HTML 文件中使用 <script type="module">
标签,指向你的源代码,Parcel 将在需要时自动生成 nomodule
版本。
<script type="module" src="app.js"></script>
变为:
<script type="module" src="app.c9a6fe.js"></script>
<script nomodule src="app.f7d631.js"></script>
这是基于你在 package.json 文件的 "browserslist"
键中声明的浏览器目标自动发生的。如果某些目标不原生支持 ES 模块,将自动编译 nomodule
版本。
改进的 JSX 支持
#Parcel 一直开箱即用地支持 JSX,甚至自动检测你正在使用的库(例如 React、Preact 等)并相应地编译。在这个版本中,Parcel 现在支持 React 17 中添加的新 JSX 运行时。这是完全自动的 - Parcel 检测你的代码使用的 React 版本并相应地选择正确的 JSX 运行时。新的 JSX 运行时应该会导致更小的包和性能改进。你也不再需要在每个文件中手动导入 React 以使用 JSX。请阅读 React 团队的博客文章以获取更多信息。
此外,Parcel 现在将读取项目中的 tsconfig.json
或 jsconfig.json
文件,这允许你配置 JSX 编译的几个方面。你可以使用它来覆盖 JSX 运行时、库和其他属性,以及启用装饰器等实验性 JavaScript 特性。请参阅 TypeScript 团队的 TSConfig 参考以获取更多信息。
改进的 Worker 支持
#Parcel 长期以来一直支持 Web Worker 和服务工作者,在这个版本中,我们做了一些重大改进。类似于 HTML 中的 <script>
标签,浏览器实际上支持两种类型的 worker:经典脚本 worker 和 ES 模块 worker。Parcel 历史上将所有 worker 视为模块,但在这个版本中,我们现在匹配原生浏览器行为并区别对待它们。
要创建使用 import
和 export
等 ES 模块语法的 worker,现在在构造 worker 时需要 type: 'module'
选项。此外,现在需要使用 URL
构造函数来创建 worker,而不是传递字符串字面量。这些更改提高了 Parcel 与原生浏览器行为以及生态系统中其他工具的兼容性。
new Worker(new URL("worker.js", import.meta.url), { type: "module" });
此外,在模块 worker 中,不再支持 importScripts
,应该替换为 import
语句或动态 import()
。
Parcel 将显示诊断以帮助你将现有代码迁移到新模式。
Worklet 支持
#除了 Web Worker 和服务工作者,Parcel 现在还支持 worklet,包括 CSS Houdini 绘制 worklet 以及 Web 音频 worklet。
绘制 worklet 使用以下语法自动检测:
CSS.paintWorklet.addModule(new URL("worklet.js", import.meta.url));
Web 音频 worklet 无法静态分析,因此对于这些,你可以使用 worklet:
命名管道来获取为正确环境编译的 worklet 文件的 URL。
import workletUrl from "worklet:./worklet.js";
context.audioWorklet.addModule(workletUrl);
改进的库构建
#在这个版本中,Parcel 的库构建支持得到了改进。
Parcel 现在支持 Node.js 用于指示模块是 ESM 还是 CommonJS 的 .mjs
和 .cjs
文件扩展名。此外,还支持 package.json 中的 "type": "module"
字段。这些提示帮助 Parcel 自动确定编译源代码时要使用的输出格式。如果你尝试在 package.json 字段中使用不支持的文件扩展名,你将收到诊断。
此外,当你在代码中使用 worker 或 URL 依赖时,Parcel 现在在构建库时会生成可静态分析的输出。这使得你的库在稍后作为应用程序使用时可以被 Parcel 或其他打包器打包。URL 依赖编译为 URL
构造函数,这在 Node 和浏览器中都原生支持,并且被许多打包器支持。
new URL("some-code.js", import.meta.url);
最后,我们改进了与库相关的许多情况的诊断。例如,当库构建中禁用作用域提升时,或使用不支持的输出格式时,你将收到诊断。我们添加的另一个有用的诊断是,当你的代码中有一个未在 package.json 中声明的外部依赖时,会通知你。这有助于防止在下游使用者使用你的库时会破坏的代码被发布。
杂项 JavaScript 改进
#同一文件中的多种依赖类型
#以前,在同一个文件中对同一个指定符使用多种类型的依赖是不可能的。例如,一个文件不能同时有相同指定符的静态导入和动态导入。现在这是正确支持的。
import something from "./foo";
import("./foo");
解构 process.env
#现在可以使用解构语法处理 process.env。
let { NODE_ENV, API_TOKEN } = process.env;
支持独立的 import.meta
#Parcel 现在更完整地支持 import.meta
。当单独引用 import.meta
时,你现在将得到一个包含 url
属性的对象。import.meta.url
直接编译为相同的 URL,没有中间对象。URL 是一个 file://
URL,包含从项目根目录到包含 import.meta.url
引用的文件的相对路径。
console.log(import.meta);
// => {url: 'file:///src/App.js'}
console.log(import.meta.url);
// => 'file:///src/App.js'
此外,import.meta.url
可以在构造 worker 时用作自引用,在你希望启动 worker 的文件本身也是 worker 的情况下。
new Worker(import.meta.url, { type: "module" });
Glob 解析器插件
#Parcel 2 现在有一个 glob 解析器插件,其工作方式类似于 Parcel 1 中的 glob 导入支持。这允许你一次导入多个文件,并得到一个键对应文件的对象。
import * as files from "./files/*.js";
console.log(files.foo);
等价于:
import * as foo from "./files/foo.js";
import * as bar from "./files/bar.js";
let files = {
foo,
bar,
};
这也适用于 url:
等管道,以及动态导入。
let files = import("./files/*.js");
async function doSomething() {
let foo = await files.foo();
let bar = await files.bar();
return foo + bar;
}
由于 glob 导入是非标准的,它们不包含在默认的 Parcel 配置中。要启用它们,请在 .parcelrc
中添加 @parcel/resolver-glob
。
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}
性能改进
#在我们上一个版本中,我们宣布了用 Rust 编写的新 JavaScript 编译器,它将性能提高了最多 10 倍。这个版本继续我们改进性能的工作,总体上比之前的测试版快约 38%。有许多小的变化累积起来,但下面描述了一些亮点。
LMDB
#Parcel 将其所做的一切缓存在文件系统上。这通过避免重复工作提高了重新启动 Parcel 时的性能。此外,文件系统在构建期间用作临时存储区,用于工作线程之间通信。这还使 Parcel 构建能够扩展到超出可用内存的大型项目。因此,从文件系统写入和读取的性能对 Parcel 构建的整体性能非常重要。
在这个版本中,我们用由 LMDB 支持的存储替换了文件系统缓存实现。LMDB 是一个用 C 编写的极其快速的嵌入式键值存储。特别是,我们使用了出色的 lmdb-store Node 模块。它使用单个内存映射文件,并自动在异步后台线程中处理写入。
使用 LMDB 而不是文件系统作为我们的缓存,性能提高了约 20%!
xxHash
#Parcel 在许多方面都严重依赖哈希,包括缓存键、内容哈希等。我们之前主要依赖内置的 Node md5 函数进行大多数哈希,但在这个版本中,我们将其替换为 xxHash。xxHash 是一个极其快速的哈希函数,以 RAM 速度限制运行。我们围绕 xxHash 的 Rust 实现构建了一个自定义 Node 包装器,比 md5 快约 7 倍。总体上,这将 Parcel 构建性能提高了约 5%。
源映射改进
#Parcel 使用我们自己的库(用 Rust 编写)来操作源映射。从缓存中序列化和反序列化大型源映射可能需要相当长的时间。在这个版本中,我们将 Serde 和 Bincode 替换为 rkyv,这是 Rust 的一个极其快速的零拷贝反序列化框架。读取缓存的源映射时,这大约快 2.5 倍,相当于整体 Parcel 构建速度提高约 5%。
缓存改进
#可靠的缓存一直是 Parcel 2 的一个重点关注领域,如我们之前的博客文章所述。在这个版本中,我们继续这项工作,并完成了 Parcel 2 所有部分到我们基于请求的缓存架构的迁移。
特别是,打包和优化包是 Parcel 中最后剩余的未完全可靠的阶段。现在,当打包器和优化器插件的配置更改时,我们正确地使缓存失效。此外,只有实际更改的包才会重新打包和写入磁盘,这提高了性能。如果 dist 目录被删除或包被手动修改,Parcel 将检测到这一点,并自动将之前打包的包从缓存复制回 dist 目录。
最后,缓存现在对位置和平台变化具有弹性。例如,如果你的项目移动到文件系统上的不同位置,或克隆到不同的机器,现在可以重用缓存。所有引用的文件路径现在都相对于项目根目录存储,而不是作为绝对路径。它们还存储了与平台无关的分隔符,这意味着缓存可以在 POSIX 和 Windows 机器之间共享。
API 变更
#在这个版本中,我们对插件 API 进行了重大更改。如果你有任何现有插件,你可能需要更新它们。由于这是一个发布候选版本,我们致力于在此版本之后避免破坏性的 API 更改。
这些更改主要是为了改进 API 的一致性、可读性、类型安全性和可理解性。例如,所有插件类型支持的 loadConfig
API 现在是完全类型化的。此外,资源和包 API 减少了对布尔标志的依赖,并将这些标志替换为一组更小的枚举,指示哪些选项实际上是互斥的。已经有很多变化,请查看 API 差异此处。
最后,我们现在随所有公共包发布 TypeScript 定义。这包括插件 API 以及 Parcel 核心的编程 API。这应该在使用 TypeScript 编写插件以及在 VSCode 等编辑器中使用普通 JavaScript 时都有帮助。
杂项
#这个版本包括许多其他小的改进和 bug 修复。下面是一些亮点。有关完整的更改列表,请参见提交差异,或 GitHub 里程碑中修复的 bug 列表。
SVG 优化器
#Parcel 现在包含一个基于 SVGO 的 SVG 优化器插件。这自动减小构建中引用的独立 .svg
文件的文件大小。可以使用 svgo.config.json
或 svgo.config.js
文件进行配置。
此外,SVGO 还用于 HTML 文件中嵌入的 SVG。在这个版本中,与此相关的还有几个 bug 修复。
图像引用
#Parcel 2 不再需要引用图像文件时使用 url:
前缀。这些可以在 JavaScript 中导入或从 HTML 或 CSS 中引用,并将生成包含内容哈希的转换 URL,用于长期缓存。
Parcel 还包含一个图像转换器,你可以通过使用查询参数启用它。这允许你自动从源文件调整图像大小或转换图像。例如,在 HTML 中,你可以多次引用同一图像,使用不同的查询参数,以不同格式生成多个不同的表示。
<picture>
<source src="snow.jpg?as=webp&width=400" type="image/webp" />
<source src="snow.jpg?as=jpg&width=400" type="image/jpeg" />
<img src="snow.jpg?as=jpg&width=400" alt="snow" />
</picture>
Web 清单
#Parcel 支持 Web 清单文件,使 PWA 能够安装在用户的设备上。这些从 HTML 文件中使用 <link rel="manifest">
标签引用。Parcel 自动处理这些文件中的 URL 引用,就像任何其他依赖一样。在这个版本中,你现在可以使用 .json
文件而不是 .webmanifest
文件,许多开发者更喜欢这种方式。
试用!
#请试用发布候选版本并给我们反馈GitHub。我们将在接下来的一个月左右时间里修复 bug,并改进文档,然后再进行稳定版本。你也可以向我们的开放集体捐款,以支持我们的贡献者。谢谢!