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

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

Parcel CSS 的性能明显优于现有工具,同时还提高了压缩质量。除了压缩之外,Parcel CSS 还处理 CSS 模块编译、树摇、自动为您的浏览器目标添加和删除供应商前缀,以及转译现代 CSS 功能,例如嵌套、逻辑属性、级别 4 颜色语法等等。

它可以与 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 包,这是一个由 Mozilla 创建并用于 Firefox 的浏览器级 CSS 词法分析器。这提供了一个坚实的基础,包括词法分析和基本解析。但是,它不会解释任何 CSS 属性或 @ 规则。这就是 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 模块,但您可能仍然需要 PostCSS 来使用更自定义的插件,例如 TailwindCSS。如果是这种情况,只需在 @parcel/transformer-css-experimental 之前添加 @parcel/transformer-postcss,您的 PostCSS 配置将自动被拾取。您可以从您的 PostCSS 配置中删除上面列出的插件,它们将由 Parcel CSS 处理。

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

您还可以尝试使用 parcel_css Rust 包,它可以让您完全访问解析后的 AST,并允许您构建自定义工具。更多 API 文档即将推出,但现在,您需要从 StyleSheet API 开始。请注意,虽然 JavaScript API 是稳定的,但 Rust API 仍然处于 alpha 阶段,结构可能会在版本之间发生变化,因为我们一直在改进 Parcel CSS。

请告诉我们您的使用情况!您可以在 GitHub 上提交有关错误或功能请求的问题。