在设计页面时,通常一个类应该具有另一个类的所有样式,以及它自己的特定样式。例如,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;
}
}
编译为 CSS 结果:
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;
}
编译为 CSS 结果:
CSS
.error:hover, .error--serious:hover {
background-color: #fee;
}
.error--serious {
border-width: 3px;
}
如何工作
与mixins不同,mixins将样式复制到当前样式规则中,而是 @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;
}
编译为 CSS 结果:
CSS
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;
}
占位符选择器
有时您想编写仅用于扩展的样式规则。在这种情况下,您可以使用占位符选择器,它们看起来像以%
而不是开头的类选择器.
。CSS 输出中不包括任何包含占位符的选择器,但扩展它们的选择器则包括在内。
SCSS
.alert:hover, %strong-alert {
font-weight: bold;
}
%strong-alert:hover {
color: red;
}
编译为 CSS 结果:
CSS
.alert:hover {
font-weight: bold;
}
私有占位符
与模块成员一样,可以通过以-
或开头的占位符选择器将其标记为私有_
。私有占位符选择器只能在定义它的样式表中扩展。对于任何其他样式表,看起来好像该选择器不存在。
扩展范围
当一个样式表扩展选择器时,该扩展将仅影响在上游模块中编写的样式规则,即该样式表使用 @use 规则或@forward规则加载的模块、这些模块加载的模块等。这有助于使@extend规则更可预测,确保它们仅影响您在编写规则时知道的样式。
如果您使用的是@import规则,则扩展根本不限定范围。它们不仅会影响您导入的每个样式表,还会影响导入样式表的每个样式表、这些样式表导入的其他所有内容,等等。如果没有@use,扩展是全局的。
局限性
@extend 只能扩展简单的选择器(如 .info 或 a)等单个选择器,类似.message.info 这种选择器。
SCSS
.alert {
@extend .message.info;
// ^^^^^^^^^^^^^
// Error: Write @extend .message, .info instead.
@extend .main .info;
// ^^^^^^^^^^^
// Error: write @extend .info instead.
}
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;
}
编译为 CSS 结果:
实例
header .warning li, header .warning aside .notice dd, aside .notice header .warning dd {
font-weight: bold;
}
扩展为 @media
虽然@extend允许在 @media 和其他 CSS 规则中,但不允许扩展出现在其规则之外的选择器。这是因为扩展选择器仅适用于给定的媒体上下文,并且无法确保在不复制整个样式规则的情况下在生成的选择器中保留限制。
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;
}
分享笔记