@extend

在设计页面时,经常会遇到一个类应该具有另一个类所有样式以及自身特定样式的情况。例如,BEM 方法 鼓励在与块或元素类相同的元素上使用修饰符类。但这会导致 HTML 混乱,容易出现忘记包含两个类而导致的错误,并且可能将非语义的样式问题引入您的标记。

<div class="error error--serious">
  Oh no! You've been hacked!
</div>
.error {
  border: 1px #f00;
  background-color: #fdd;
}

.error--serious {
  border-width: 3px;
}

Sass 的 @extend 规则解决了这个问题。它写成 @extend <selector>,它告诉 Sass 一个选择器应该继承另一个选择器的样式。

游乐场

SCSS 语法

.error {
  border: 1px #f00;
  background-color: #fdd;

  &--serious {
    @extend .error;
    border-width: 3px;
  }
}
游乐场

Sass 语法

.error
  border: 1px #f00
  background-color: #fdd

  &--serious
    @extend .error
    border-width: 3px


CSS 输出

.error, .error--serious {
  border: 1px #f00;
  background-color: #fdd;
}
.error--serious {
  border-width: 3px;
}


当一个类扩展另一个类时,Sass 会将与扩展器匹配的所有元素都像也与被扩展的类匹配一样进行样式设置。当一个类选择器扩展另一个类选择器时,它的工作方式与您将扩展的类添加到 HTML 中已经具有扩展类的每个元素完全相同。您只需编写 class="error--serious",Sass 就会确保它的样式设置与它也具有 class="error" 一样。

当然,选择器不仅仅在样式规则中单独使用。Sass 知道在 任何 使用选择器的地方进行扩展。这确保了您的元素的样式设置与它们匹配扩展的选择器完全相同。

游乐场

SCSS 语法

.error:hover {
  background-color: #fee;
}

.error--serious {
  @extend .error;
  border-width: 3px;
}
游乐场

Sass 语法

.error:hover
  background-color: #fee


.error--serious
  @extend .error
  border-width: 3px

CSS 输出

.error:hover, .error--serious:hover {
  background-color: #fee;
}

.error--serious {
  border-width: 3px;
}

⚠️ 注意!

扩展是在编译完样式表的其余部分后解析的。特别是,它发生在解析 父选择器 之后。这意味着,如果您 @extend .error,它不会影响 .error { &__icon { ... } } 中的内部选择器。这也意味着 SassScript 中的父选择器 无法查看扩展的结果。

工作原理工作原理永久链接

mixin(将样式复制到当前样式规则中)不同,@extend 会更新包含扩展选择器的样式规则,使其也包含扩展选择器。在扩展选择器时,Sass 会进行 智能统一

  • 它永远不会生成诸如 #main#footer 之类的选择器,这些选择器不可能与任何元素匹配。

  • 它确保复杂选择器交错,以便它们无论 HTML 元素嵌套的顺序如何都能正常工作。

  • 它尽可能地修剪冗余选择器,同时仍然确保特异性大于或等于扩展器的特异性。

  • 它知道一个选择器何时匹配另一个选择器的所有内容,并且可以将它们组合在一起。

  • 它智能地处理 组合器通用选择器包含选择器的伪类

游乐场

SCSS 语法

.content nav.sidebar {
  @extend .info;
}

// This won't be extended, because `p` is incompatible with `nav`.
p.info {
  background-color: #dee9fc;
}

// There's no way to know whether `<div class="guide">` will be inside or
// outside `<div class="content">`, so Sass generates both to be safe.
.guide .info {
  border: 1px solid rgba(#000, 0.8);
  border-radius: 2px;
}

// Sass knows that every element matching "main.content" also matches ".content"
// and avoids generating unnecessary interleaved selectors.
main.content .info {
  font-size: 0.8em;
}
游乐场

Sass 语法

.content nav.sidebar
  @extend .info


// This won't be extended, because `p` is incompatible with `nav`.
p.info
  background-color: #dee9fc


// There's no way to know whether `<div class="guide">` will be inside or
// outside `<div class="content">`, so Sass generates both to be safe.
.guide .info
  border: 1px solid rgba(#000, 0.8)
  border-radius: 2px


// Sass knows that every element matching "main.content" also matches ".content"
// and avoids generating unnecessary interleaved selectors.
main.content .info
  font-size: 0.8em

CSS 输出

p.info {
  background-color: #dee9fc;
}

.guide .info, .guide .content nav.sidebar, .content .guide nav.sidebar {
  border: 1px solid rgba(0, 0, 0, 0.8);
  border-radius: 2px;
}

main.content .info, main.content nav.sidebar {
  font-size: 0.8em;
}









💡 有趣的事实

您可以使用 选择器函数 直接访问 Sass 的智能统一!selector.unify() 函数 返回一个匹配两个选择器交集的选择器,而 selector.extend() 函数 的工作原理与 @extend 相同,但作用于单个选择器。

⚠️ 注意!

因为 @extend 会更新包含扩展选择器的样式规则,所以它们的样式在 级联 中的优先级取决于扩展选择器的样式规则出现的位置,而不是 基于 @extend 出现的位置。这可能会令人困惑,但请记住:这与如果您将扩展的类添加到 HTML 中,这些规则将具有的优先级相同!

占位符选择器占位符选择器永久链接

有时您希望编写一个 用于扩展的样式规则。在这种情况下,您可以使用 占位符选择器,它们看起来像以 % 而不是 . 开头的类选择器。任何包含占位符的选择器都不会包含在 CSS 输出中,但扩展它们的选择器除外。

游乐场

SCSS 语法

.alert:hover, %strong-alert {
  font-weight: bold;
}

%strong-alert:hover {
  color: red;
}
游乐场

Sass 语法

.alert:hover, %strong-alert
  font-weight: bold


%strong-alert:hover
  color: red

CSS 输出

.alert:hover {
  font-weight: bold;
}




私有占位符私有占位符永久链接

模块成员 一样,占位符选择器可以通过在其名称开头使用 -_ 来标记为私有。私有占位符选择器只能在定义它的样式表中扩展。对于任何其他样式表,它看起来就像该选择器不存在一样。

扩展范围扩展范围永久链接

当一个样式表扩展一个选择器时,该扩展只会影响在 上游 模块中编写的样式规则——也就是说,使用 @use 规则@forward 规则 由该样式表加载的模块、这些 模块加载的模块等等。这有助于使您的 @extend 规则更易于预测,确保它们只影响您在编写它们时意识到的样式。

⚠️ 注意!

如果您使用 @import 规则,则扩展根本没有范围。它们不仅会影响您导入的每个样式表,还会影响导入您的样式表的每个样式表、这些样式表导入的所有其他内容等等。如果没有 @use,扩展是 全局的

强制和可选扩展强制和可选扩展永久链接

通常,如果 @extend 不匹配样式表中的任何选择器,Sass 会产生错误。这有助于防止打字错误或重命名选择器而没有重命名继承自它的选择器。需要扩展选择器存在才能生效的扩展是 强制 的。

但这可能并不总是您想要的。如果您希望 @extend 在扩展选择器不存在时不执行任何操作,只需在末尾添加 !optional

扩展或 Mixin?扩展或 Mixin?永久链接

扩展和 mixin 都是封装和重复使用 Sass 中样式的方法,这自然会引发何时使用哪一个的问题。当您需要使用 参数 配置样式时,mixin 显然是必要的,但如果它们只是一块样式呢?

根据经验法则,当您表达语义类(或其他语义选择器)之间的关系时,扩展是最佳选择。因为具有类 .error--serious 的元素 一个错误,所以它扩展 .error 是有意义的。但是对于非语义的样式集合,编写 mixin 可以避免级联问题,并使其更容易在以后进行配置。

💡 有趣的事实

大多数 Web 服务器使用非常擅长处理重复的相同文本块的算法来压缩它们提供的 CSS。这意味着,尽管 mixin 可能会产生比扩展更多的 CSS,但它们可能不会实质性地增加用户需要下载的数量。因此,请选择最适合您的用例的功能,而不是生成最少 CSS 的功能!

局限性局限性永久链接

不允许的选择器不允许的选择器永久链接

兼容性(无复合扩展)
Dart Sass
LibSass
Ruby Sass

LibSass 和 Ruby Sass 目前允许扩展诸如 .message.info 之类的复合选择器。但是,此行为与 @extend 的定义不符:它不是将与扩展选择器匹配的元素的样式设置得好像它具有 class="message info" 一样,这将受包含 .message .info 的样式规则的影响,而只是将其样式设置为包含 .message info 的规则。

为了使 @extend 的定义简单易懂,并保持实现简洁高效,此行为现已弃用,并将从未来的版本中删除。

有关更多详细信息,请参阅 重大更改页面

只有 简单选择器——诸如 .infoa 之类的单个选择器——才能扩展。如果可以扩展 .message.info,则 @extend 的定义指出与扩展器匹配的元素将被设置为好像它们匹配 .message.info 一样。这与匹配 .message.info 都相同,因此编写它而不是 @extend .message, .info 没有任何好处。

同样,如果可以扩展 .main .info,它将执行与单独扩展 .info(几乎)相同的事情。细微的差异不值得混淆,因为它看起来像是在做一些实质上不同的东西,因此也不允许这样做。

游乐场

SCSS 语法

.alert {
  @extend .message.info;
  //      ^^^^^^^^^^^^^
  // Error: Write @extend .message, .info instead.

  @extend .main .info;
  //      ^^^^^^^^^^^
  // Error: write @extend .info instead.
}
游乐场

Sass 语法

.alert
  @extend .message.info
  //      ^^^^^^^^^^^^^
  // Error: Write @extend .message, .info instead.

  @extend .main .info
  //      ^^^^^^^^^^^
  // Error: write @extend .info instead.

HTML 启发式方法HTML 启发式方法永久链接

@extend交织复杂的选择器时,它不会生成祖先选择器所有可能的组合。它可能生成的许多选择器不太可能实际匹配真实的HTML,并且生成所有这些选择器会使样式表变得过大,而实际价值却很小。相反,它使用了一种启发式方法:它假设每个选择器的祖先都是自包含的,不会与任何其他选择器的祖先交织在一起。

游乐场

SCSS 语法

header .warning li {
  font-weight: bold;
}

aside .notice dd {
  // Sass doesn't generate CSS to match the <dd> in
  //
  // <header>
  //   <aside>
  //     <div class="warning">
  //       <div class="notice">
  //         <dd>...</dd>
  //       </div>
  //     </div>
  //   </aside>
  // </header>
  //
  // because matching all elements like that would require us to generate nine
  // new selectors instead of just two.
  @extend li;
}
游乐场

Sass 语法

header .warning li
  font-weight: bold


aside .notice dd
  // Sass doesn't generate CSS to match the <dd> in
  //
  // <header>
  //   <aside>
  //     <div class="warning">
  //       <div class="notice">
  //         <dd>...</dd>
  //       </div>
  //     </div>
  //   </aside>
  // </header>
  //
  // because matching all elements like that would require us to generate nine
  // new selectors instead of just two.
  @extend li

CSS 输出

header .warning li, header .warning aside .notice dd, aside .notice header .warning dd {
  font-weight: bold;
}


















@media中扩展在 @media 中扩展 永久链接

虽然允许在@media和其他CSS at-规则中使用@extend,但不允许扩展出现在其 at-规则之外的选择器。这是因为扩展的选择器仅在给定的媒体上下文中应用,并且无法确保在生成的选​​择器中保留该限制,而无需复制整个样式规则。

游乐场

SCSS 语法

@media screen and (max-width: 600px) {
  .error--serious {
    @extend .error;
    //      ^^^^^^
    // Error: ".error" was extended in @media, but used outside it.
  }
}

.error {
  border: 1px #f00;
  background-color: #fdd;
}
游乐场

Sass 语法

@media screen and (max-width: 600px)
  .error--serious
    @extend .error
    //      ^^^^^^
    // Error: ".error" was extended in @media, but used outside it.



.error
  border: 1px #f00
  background-color: #fdd