宣布 Dart Sass

发布于 2016 年 10 月 31 日 作者:Natalie Weizenbaum

在过去的几个月里,我一直在秘密地进行一个新项目。今天,我准备向全世界宣布 Dart Sass。这是一个全新的 Sass 实现,旨在快速、易于安装和易于修改。它尚未完成——我正在稳步地完成 sass-spec 的工作——所以今天我发布的是 1.0.0-alpha.1 版本。但它足够稳定,您可以下载、试用并 开始提交 问题

您可以从 发布页面 下载独立的归档文件——只需解压它,将文件夹添加到您的路径中,然后运行 dart-sass。Dart 也可以编译成 JavaScript,因此如果您安装了 npm,则可以通过运行 npm install -g dart-sass 来安装 JS 版本。而且,如果您碰巧是 Dart 用户,则可以使用 pub global install sass 来安装它。

为什么要重写 Sass?为什么要重写 Sass? 永久链接

在过去的几年里,Sass 有两个主要的实现。Ruby Sass 是最初的版本,主要由我编写,并在 Chris 的大力帮助下完成。它属于高级实现,易于修改,因此我们在这里迭代新功能,并且新功能也首先在这里发布。然后是 LibSass,即 C++ 实现,最初由 AaronHampton 创建,现在由 MarcelMichael 维护。它属于低级实现,这使得它非常快且易于安装并嵌入到其他语言中。特别是,它的 Node.js 绑定 是一种在 JavaScript 世界中使用 Sass 的非常流行的方式。

每个实现的优势都弥补了另一个实现的弱点。在 LibSass 快速且可移植的地方,Ruby Sass 缓慢且对于非 Ruby 用户来说难以安装。在 Ruby Sass 易于迭代的地方,LibSass 的低级语言使得添加新功能变得相当困难。互补关系可能是健康的,但也可能意味着两种解决方案都不如需要的那样好。这就是我们在 5 月份发现的情况,当时 Marcel 正式离开了 LibSass 团队[1]

如果没有两个人投入的精力,我们就无法确定 LibSass 是否能够跟上 Chris 和我想对语言进行更改的速度。而且很长时间以来,很明显 Ruby Sass 对于涉及大型样式表的使用案例来说太慢了。我们需要一个新的实现,一个既能快速生成 CSS 又能快速添加新功能 的实现。

为什么选择 Dart?为什么选择 Dart?永久链接

我们考虑了许多可能的语言,最终出于多种原因选择了 Dart。首先,它非常快——Dart VM 通常比 JavaScript VM 快得多,并且 早期基准测试[2] 表明,对于大型样式表,Dart Sass 比 Ruby Sass 快 5-10 倍,并且仅比 LibSass 慢约 1.5 倍。我猜它可能会比惯用的 JS 实现快 1.5-2 倍,但我无法确定。而且 Dart 的性能一直在不断提高 

同时,Dart 易于使用——比 C++ 易于使用得多,在某种程度上甚至比 Ruby 对于这样一个大型项目来说更容易使用。当然,熟悉它的人不如熟悉 JavaScript 的人多,但语言实现往往不会获得很多外部贡献。我将负责新实现的大部分工作,而 Dart 是我目前最舒适的语言(在我不从事 Sass 工作时,我在 Dart 团队工作)。使用 Dart 为我提供了很多额外的 速度。

与 Ruby 或 JavaScript 不同,Dart 是静态类型的,因此可以在不运行代码的情况下确定每个值的类型。与 C++ 不同,它是垃圾回收的,因此我们不必过多地担心清理工作。这使得它易于编写、易于修改和易于维护。也许更重要的是,它使它易于转换为其他编程语言,这将有助于 LibSass 更快地获得新功能 

我们选择 Dart 的最后一个原因是只有其他少数几种语言可以夸耀的:JavaScript 兼容性。Dart 可以编译成 JavaScript,可以直接在 Node.js 中使用,甚至可能在浏览器中运行。很大一部分 Sass 生态系统构建在 node-sass 之上,我们打算使 Dart Sass 的 JS 版本尽可能与 node-sass 的 API 兼容,以便它可以轻松地集成到现有的工具和构建 系统中。

唯一的缺点是存在速度损失:Dart Sass 在 V8 上运行的速度大约是它在 Dart VM 上运行速度的两倍。但是,这仍然使其比 Ruby Sass 快 3-4 倍。最终,我们还希望为 JS 编译版本的用户提供一条轻松的路径,以便尽可能减少摩擦地迁移到 Dart VM 版本 

其他实现将发生什么?其他实现将发生什么? 永久链接

LibSass 的开发没有任何变化。Michael 正在努力添加来自 Sass 3.5 的功能,并且我们预计随着添加新的语言功能,此过程将继续进行。唯一的区别是 LibSass 将不再需要严格兼容最新版本的语言才能发布该版本,因为它将不再是唯一具有合理 性能的实现。

更大的灵活性转化为更快的 LibSass 版本,这些版本优先考虑用户最需要的功能。严格的兼容性意味着诸如 CSS 自定义属性支持 之类的重要功能无法发布,直到相应的 Ruby Sass 版本中所有细微的棘手边缘情况(例如 :root 统一)也得到实现。我们仍然会努力实现尽可能多的兼容性,但我们不会让这一点阻碍 速度。

另一方面,除非出现新的维护者,否则 Ruby Sass 最终将消失。我们不想让过渡突然发生并导致生态系统分裂:Chris 和我承诺维护它一年,包括使语言与 Dart Sass 中的任何新添加内容保持同步。如果有人有兴趣在该期限后自愿担任维护者,我们将非常乐意指导他们并在未来一年内向他们教授代码库。但如果没有人站出来,Ruby Sass 将被正式视为已弃用并 不再维护。

我想强调的是,我们并非轻易做出停止开发 Ruby Sass 的决定。这是一个很大的变化,对我来说并不容易——我已经连续近十年致力于 Ruby Sass 的开发,放弃这段历史很难。但 Chris 和我对此进行了充分的讨论,我们相信这是正确的举动。我们只有这么多时间可以投入到 Sass 中,将这些时间投入到一个速度慢到许多最大的用户都无法使用的实现中已经没有意义了 

接下来是什么?接下来是什么?永久链接

在我们发布 Dart Sass 的第一个稳定版本之前,我们的待办事项清单上还有几件大事 

  • 完整的 sass-spec 兼容性。语言中仍然有很多角落 Dart Sass 做错了事情,尤其是在 @extend 方面。我不认为任何单个不兼容性都特别难以解决,而且 sass-spec 非常全面,所以这只是稳步减少失败规范数量的问题,直到它达到 零。

  • 在 npm 包中实现与 node-sass render() 足够接近的兼容性。node-sass render() API 是 JavaScript 世界中 LibSass 的主要入口点。它是构建系统运行 Sass 的方式,用户定义自定义 Sass 函数的方式,以及 Eyeglass 将模块传递给 Sass 的方式。我们希望以足够高的保真度支持此 API,以便现有生态系统能够与 JS 编译的 Dart Sass 协同工作。

  • 在 Ruby Sass 中实现 Dart Sass 兼容性。在某些情况下,Dart Sass 故意与 Ruby Sass 存在差异,尤其是在 Ruby Sass 的行为被认为是错误的情况下。我们应该在 Ruby Sass 中添加弃用消息,并且如果我们能够以最小的干扰来做到这一点,则添加对新 行为的支持。

我们最终还想做更多的事情,例如在浏览器中支持 Sass 以及为 Dart VM 上的 Sass 提供与 node-sass 兼容的包装器,但这些并不妨碍初始 发布。

迈向未来迈向未来 永久链接

未来几个月将会有大量工作用于使 Dart Sass 稳定和兼容,并将 Sass 3.5 功能 集成到 LibSass 中。我认为 2017 年初很可能会发布 Dart Sass 的稳定版本和 LibSass 的 3.5 版本。届时,我们将把目光投向重大功能,并开始朝着 Sass 4.0 及其全新的模块 系统努力。

Dart Sass 是一项重大变化,但它也令人兴奋。它将使我们能够更快地将新功能交付到用户手中,并使这些功能运行得更快。它将使用户能够轻松地安装和运行参考实现。而且它将首次为我们提供一种在纯 JavaScript Sass 中运行 Sass 的高性能方法。好处是巨大而切实的,我相信它们值得付出 代价。


  1. 我之所以说“正式”,是因为他仍然在力所能及的情况下为项目做出贡献,只是不再以正式维护者的身份。 ↩︎

  2. 存在一些注意事项:我不是基准测试专家,这些测试是临时的,并且针对非代表性的源样式表运行。如果有人有兴趣参与更科学的基准测试,请告诉我! ↩︎