宣布 Parcel CSS:一个用 Rust 编写的全新 CSS 解析器、编译器和压缩器!

我非常高兴地宣布 @parcel/css,这是一个用 Rust 编写的全新 CSS 解析器、编译器和压缩器!可以在 GitHub 上查看,或在浏览器中尝试 在线演示

与现有工具相比,Parcel CSS 的性能显著更好,同时还改进了压缩质量。除了压缩之外,Parcel CSS 还能处理 CSS 模块、树摇、根据浏览器目标自动添加和删除供应商前缀,以及转译现代 CSS 特性,如嵌套、逻辑属性、四级颜色语法等等。

它可以与 Parcel 一起使用,作为 JavaScript 或 Rust 的独立库,或作为任何其他工具的插件。这个 Rust 库被设计为 CSS 工具的平台,可以访问所有 CSS 规则、选择器、属性和值的完全解析的数据结构。

性能

#

Parcel CSS 极其快速。对于压缩,它比 CSSNano 快 100 多倍,比 ESBuild 快 3 倍多。它可以在单线程上每秒压缩超过 270 万行代码。下面的示例展示了压缩 Bootstrap 4(约 10,000 行)的基准测试。

Performance CSSNano ESBuild Parcel CSS 0ms 150ms 300ms 450ms 600ms 4.6ms 17.41ms 542.96ms

尽管速度极快,但 Parcel CSS 并不会在大小上妥协。在许多情况下,它可以生成比其他工具更小的输出,这得益于将许多库中使用的传统 CSS 语法转换为更小的现代语法的能力,以及对每个单独 CSS 属性的完全理解。

Size CSSNano ESBuild Parcel CSS 0 KB 40 KB 80 KB 120 KB 160 KB 139.8 KB 156.57 KB 155.89 KB

Parcel CSS 之所以快,不仅是因为它是用本地语言编写的,还因为它从一开始就考虑了性能。它被设计为高效地使用内存,包括诸如使用单字节位标志表示供应商前缀,以及将所有 CSS 属性解析为结构化数据而不是每次使用时都需要重新解析的字符串等优化。

架构

#

Parcel CSS 基于 cssparser Rust crate,这是一个由 Mozilla 创建并在 Firefox 中使用的浏览器级 CSS 分词器。这提供了坚实的基础,包括分词和基本解析。但是,它不会解释任何 CSS 属性或 at 规则。这就是 Parcel CSS 的用武之地。它处理解析每个单独的规则和属性值,以及压缩、编译和重新打印为 CSS。

许多其他 CSS 处理器将属性值视为字符串,或者是未类型化的标记序列。这意味着每个想要处理这些值的转换器都必须自己解析和解释它们,导致重复工作和不一致。例如,PostCSS 解析的 CSS 属性的 AST 看起来像这样:

{
"type": "decl",
"prop": "background",
"value": "url(img.png) 20px 10px / 50px 100px"
}

即使使用 postcss-value-parser(许多 PostCSS 插件用于分词属性值的独立库),每个标记的含义仍然没有被解释。上面的值解析如下:

[
{
type: "function",
value: "url",
nodes: [{ type: "word", value: "img.png" }],
},
{ type: "space", value: " " },
{ type: "word", value: "20px" },
{ type: "space", value: " " },
{ type: "word", value: "10px" },
{ type: "div", value: "/" },
{ type: "word", value: "50px" },
{ type: "space", value: " " },
{ type: "word", value: "100px" },
];

虽然比字符串更有结构,更容易处理,但并不清楚 20pxbackground-position-x 的值,50px 是背景宽度的值。这需要由用户自行解释。

Parcel CSS 使用 CSS 规范的语法解析所有值,并为每个属性公开特定的值类型。例如,Parcel CSS 表示上述属性如下:

Background([Background {
image: Url(Url { url: "img.png" }),
color: CssColor(RGBA(RGBA { red: 0, green: 0, blue: 0, alpha: 0 })),
position: Position {
x: Length(Dimension(Px(20.0))),
y: Length(Dimension(Px(10.0))),
},
repeat: BackgroundRepeat {
x: Repeat,
y: Repeat,
},
size: Explicit {
width: LengthPercentage(Dimension(Px(50.0))),
height: LengthPercentage(Dimension(Px(100.0))),
},
attachment: Scroll,
origin: PaddingBox,
clip: BorderBox,
}])

这正是浏览器解析 CSS 的方式。值被解释,隐式默认值(如背景附件)被填充。这提高了性能,因为每次转换器想要处理属性时,不需要重新解析、转换和重新字符串化。这也提高了可靠性,因为每个转换器不会稍微不同地解析值,或者使用正则表达式或字符串替换等可能导致错误的捷径。

由于属性值是单独解释的,这种方法还能实现更好的压缩。例如,可以自动删除隐式默认值,可以在不需要的地方删除空白,可以在可能的情况下将长属性合并为简写属性等。

这种架构为 CSS 工具提供了基础,可以专注于以有趣的方式使用属性,而不是解析和解释它们。

试用

#

如果你正在使用 Parcel,你可以尝试使用 Parcel CSS 作为 CSS 转换器、压缩器或两者。我们希望尽快替换默认的 CSS 转换器和压缩器,但希望先获得反馈。目前,只需在 .parcelrc 文件中添加以下内容:

{
"extends": "@parcel/config-default",
"transformers": {
"*.css": ["@parcel/transformer-css-experimental"]
},
"optimizers": {
"*.css": ["@parcel/optimizer-css"]
}
}

你还应该在 package.json 中添加 browserslist 属性,定义 CSS 将编译的目标浏览器。

虽然 Parcel CSS 处理了最常用的 PostCSS 插件,如 autoprefixerpostcss-preset-env 和 CSS 模块,但对于 TailwindCSS 等更自定义的插件,你可能仍然需要 PostCSS。如果是这种情况,只需在 @parcel/transformer-css-experimental 之前添加 @parcel/transformer-postcss,你的 PostCSS 配置将自动被捕获。你可以从 PostCSS 配置中删除上面列出的插件,它们将由 Parcel CSS 处理。

如果你没有使用 Parcel,你仍然可以尝试 Parcel CSS。你可以使用 JavaScript API 独立使用它,或为你喜欢的构建工具创建一个插件。我们希望 Parcel CSS 能被许多工具采用,不仅仅是 Parcel,以便推动整个 CSS 工具生态系统向前发展。

你还可以尝试 parcel_css Rust crate,它提供对完全解析的 AST 的完全访问,并允许你构建自定义工具。更多 API 文档即将推出,但现在,你需要从 StyleSheet API 开始。请注意,虽然 JavaScript API 是稳定的,但 Rust API 仍处于 alpha 阶段,结构可能会随着我们继续改进 Parcel CSS 而在版本之间发生变化。

请告诉我们情况如何!你可以在 GitHub 上提交 bug 或功能请求。