模块系统正式发布

发布于 2019年10月2日,作者 Natalie Weizenbaum

Sass 团队多年来一直知道,@import 规则(Sass 最早添加的功能之一)并非我们期望的那样好。它给我们的用户带来了许多问题。

  • 几乎不可能找出给定的变量、mixin 或函数(统称为“成员”)最初是在哪里定义的,因为在一个样式表中定义的任何内容都可用于在其后导入的所有样式表。

  • 即使你选择显式导入定义了所用成员的每个样式表,最终也会得到重复的 CSS 和奇怪的副作用,因为样式表在每次导入时都会从头重新加载。

  • 使用简洁简单的名称是不安全的,因为始终存在应用程序中其他某个样式表使用相同名称并破坏你的逻辑的可能性。为了安全起见,用户必须手动向他们定义的所有内容添加冗长且笨拙的命名空间。

  • 库作者无法确保他们的私有辅助函数不会被下游用户访问,从而导致混乱和向后兼容性问题。

  • @extend 规则可以影响样式表中任何地方的任何选择器,而不仅仅是其作者显式选择扩展的选择器。

我们还知道,我们想要引入的任何替代方案都必须经过精心设计和开发,以确保它能为 Sass 的未来发展提供坚实的基础。在过去的几年里,我们讨论、设计和开发了一个全新的模块系统来解决这些问题以及更多问题,今天我们很高兴地宣布它已在 Dart Sass 1.23.0 中可用。

请注意,模块系统是完全向后兼容的。没有删除或弃用任何现有功能,并且你当前的 Sass 样式表将继续像往常一样工作。我们设计的模块系统可以与 @import 完全互操作,以便样式表作者可以轻松地逐步迁移到它。我们确实计划最终弃用 @import,但要等到每个人都有机会迁移之后很久才会这样做。

@use,模块系统的核心@use,模块系统的核心 permalink

@use 规则是 @import 的主要替代方案:它使来自另一个样式表的 CSS、变量、mixin 和函数在当前样式表中可用。默认情况下,变量、mixin 和函数在基于该 URL 的基本名称的命名空间中可用。

@use "bootstrap";

.element {
  background-color: bootstrap.$body-bg;
  @include bootstrap.float-left;
}

除了命名空间之外,@use@import 之间还有一些重要的区别。

  • 无论样式表被使用多少次,@use 只执行一次样式表并包含其 CSS。
  • @use 只在当前样式表中提供名称,而不是全局提供。
  • 使用 @use 时,名称以 -_ 开头的成员对当前样式表是私有的。
  • 如果样式表包含 @extend,则该扩展只应用于它导入的样式表,而不是导入它的样式表。

请注意,占位符选择器没有命名空间,但它们确实尊重隐私。

控制命名空间控制命名空间 permalink

虽然 @use 规则的默认命名空间由其 URL 的基本名称确定,但也可以使用 as 显式设置它。

@use "bootstrap" as b;

.element {
  @include b.float-left;
}

特殊的构造 as * 也可以用于将所有内容包含在顶级命名空间中。请注意,如果多个模块公开了具有相同名称的成员并与 as * 一起使用,Sass 将产生错误。

@use "bootstrap" as *;

.element {
  @include float-left;
}

配置库配置库 permalink

使用 @import 时,库通常通过设置覆盖这些库定义的 !default 变量的全局变量来配置。由于变量在使用 @use 时不再是全局变量,因此它支持一种更明确的配置库方式:with 子句。

// bootstrap.scss
$paragraph-margin-bottom: 1rem !default;

p {
  margin-top: 0;
  margin-bottom: $paragraph-margin-bottom;
}
@use "bootstrap" with (
  $paragraph-margin-bottom: 1.2rem
);

这在评估 bootstrap 的 $paragraph-margin-bottom 变量之前将其设置为 1.2remwith 子句只允许在导入的模块中(或由其转发)定义的变量,并且只有在这些变量用 !default 定义时才允许,因此用户可以防止输入错误。

@forward,供库作者使用@forward,供库作者使用 permalink

@forward 规则将另一个模块的变量、mixin 和函数作为当前模块公开的 API 的一部分包含在内,而不会使它们对当前模块内的代码可见。它允许库作者能够将其库拆分成许多不同的源文件,而不会牺牲这些文件内的局部性。与 @use 不同,forward 不会向名称添加任何命名空间。

// bootstrap.scss
@forward "functions";
@forward "variables";
@forward "mixins";

可见性控制可见性控制 permalink

@forward 规则可以选择只显示特定的名称。

@forward "functions" show color-yiq;

它还可以隐藏旨在作为库私有的名称。

@forward "functions" hide assert-ascending;

额外前缀额外前缀 permalink

如果你通过一个多合一模块转发一个子模块,你可能希望向该模块添加一些手动命名空间。你可以使用 as 子句执行此操作,该子句会为每个转发的成员名称添加前缀。

// material/_index.scss
@forward "theme" as theme-*;

这样,用户就可以使用具有良好作用域的主题变量名称的多合一模块。

@use "material" with ($theme-primary: blue);

或者他们可以使用具有更简单名称的子模块。

@use "material/theme" with ($primary: blue);

内置模块内置模块 permalink

新的模块系统还添加了内置模块(sass:mathsass:colorsass:stringsass:listsass:mapsass:selectorsass:meta)来保存所有现有的内置 Sass 函数。由于这些模块通常会以命名空间导入,因此现在更容易使用 Sass 函数,而不会与普通 CSS 函数发生冲突。

这反过来也使得 Sass 添加新函数更加安全。我们预计将来会向这些模块添加许多实用函数。

已重命名的函数已重命名的函数 permalink

某些函数在内置模块中的名称与作为全局函数时的名称不同。已经具有手动命名空间的内置函数(如 map-get())在内置模块中已删除了这些命名空间,因此你只需编写 map.get() 即可。类似地,adjust-color()scale-color()change-color() 现在分别为 color.adjust()color.scale()color.change()

我们还借此机会更改了一些令人困惑的旧函数名称。unitless() 现在是 math.is-unitless()comparable() 现在是 math.compatible()

已删除的函数已删除的函数 permalink

Sass 的颜色简写函数 lighten()darken()saturate()desaturate()opacify()fade-in()transparentize()fade-out() 都有非常不直观的行为。它们不是以流畅的方式缩放其关联属性,而是只是按静态量递增它们,因此对于亮度为 85% 的颜色,lighten($color, 20%) 将返回 white,而不是返回亮度为 88% 的颜色(更接近纯白色 20%)。

为了帮助我们走上解决此问题的道路,这些函数(以及 adjust-hue())不包含在新内置模块中。你仍然可以通过调用 color.adjust() 获得相同的效果——例如,lighten($color, $amount) 等效于 color.adjust($color, $lightness: $amount)——但我们建议尽可能尝试使用 color.scale(),因为它更直观。

将来某个时候,我们计划添加 color.lighten() 和类似的函数作为 color.scale() 的简写。

meta.load-css()meta.load-css() permalink

新的模块系统带有一个新的内置 mixin,meta.load-css($url, $with: ())。此 mixin 使用给定的 URL 动态加载模块并包含其 CSS(尽管其函数、变量和 mixin 不可用)。这是嵌套导入的替代方案,它有助于解决动态导入的一些用例,而不会出现许多如果可以动态加载新成员而可能出现的问题。

@import 兼容性@import 兼容性 permalink

Sass 生态系统不会一夜之间切换到 @use,因此在此期间,它需要与 @import 良好地互操作。这在两个方向上都得到支持。

  • 当包含 @import 的文件被 @use 时,其全局命名空间中的所有内容都被视为单个模块。然后使用该模块的命名空间按正常方式引用该模块的成员。

  • 当包含 @use 的文件被 @import 时,其公共 API 中的所有内容都将添加到导入样式表的全局作用域。这允许库控制它导出的特定名称,即使对于使用 @import 而不是 @use 的用户也是如此。

为了允许库维护其现有的以 @import 为中心的 API(在必要时使用显式命名空间),此提案还添加了对仅对 @import 可见、对 @use 不可见的文件的支持。它们写成 "file.import.scss",并在用户编写 @import "file" 时导入。

自动迁移自动迁移 permalink

与新模块系统的发布同时,我们还推出了一个新的自动 Sass 迁移工具。此工具使大多数样式表可以轻松地自动迁移到使用新的模块系统。请按照 Sass 网站上的说明进行安装,然后在你的应用程序上运行它。

$ sass-migrator module --migrate-deps <path/to/style.scss>

--migrate-deps 标志告诉迁移工具不仅迁移你传递的文件,还迁移它导入的所有内容。迁移工具将自动获取通过 Webpack 的 node_modules 语法导入的文件,但你也可以使用 --load-path 标志传递显式加载路径。

如果你希望迁移工具告诉你它将进行哪些更改,而无需实际进行更改,请同时传递 --dry-run 标志和 --verbose 标志,以指示它只打印出它将进行的更改,而无需将其保存到磁盘。

迁移库迁移库 permalink

如果你想迁移一个供下游用户加载和使用的 Sass 库,请运行

$ sass-migrator module --migrate-deps --forward=all <path/to/index.scss>

如果使用--forward 标志,迁移工具会添加@forward 规则,以便用户可以通过单个 @use 加载库中定义的所有 mixin、变量和函数。

如果您为库添加了手动命名空间以避免名称冲突,则如果传递--remove-prefix 标志,迁移工具会为您将其删除。您甚至可以选择只转发最初具有该前缀的成员,方法是传递 --forward=prefixed

提交问题提交问题永久链接

迁移工具是全新的,因此可能还存在一些问题。如果您遇到任何问题,请随时在 GitHub 上提交问题

立即体验!立即体验! 永久链接

模块系统作为 Dart Sass 1.23.0 的一部分提供。您可以立即使用以下命令安装它 

$ npm install -g sass

或者,查看安装页面,了解所有不同的安装方法!

未来计划未来计划永久链接

Sass 团队希望在 @use@import 可以共存的时间内提供大量时间,以帮助生态系统顺利迁移到新系统。但是,完全弃用 @import 是为了简化、提高性能和与CSS兼容性的最终目标。因此,我们计划按照以下时间表逐步减少对 @import 的支持 

  • 在 Dart Sass 和 LibSass 都推出对模块系统支持的一年后 Dart Sass 推出对模块系统支持的两年后,以较早者为准(最迟2021 年 10 月 1 日),我们将弃用 @import 以及可以通过模块进行的全局核心库函数调用。

  • 在此弃用生效一年后(最迟2022 年 10 月 1 日),我们将完全停止对 @import 和大多数全局函数的支持。这将涉及所有实现的主要版本发布。

这意味着 @import@use 将至少有两年可以同时使用,实际上可能接近三年。

2022 年 7 月:鉴于 LibSass 在添加对新模块系统支持之前就被弃用了,弃用和删除 @import 的时间表已被推迟。我们现在打算等到 80% 的用户使用 Dart Sass(通过 npm 下载量衡量)后才弃用 @import,并在之后至少等待一年,甚至更长时间,才会完全将其删除