@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 一个选择器应该继承另一个选择器的样式。
¥Sass’s @extend
rule solves this. It’s written @extend <selector>
, and it
tells Sass that one selector should inherit the styles of another.
SCSS Syntax
.error {
border: 1px #f00;
background-color: #fdd;
&--serious {
@extend .error;
border-width: 3px;
}
}
Sass Syntax
.error
border: 1px #f00
background-color: #fdd
&--serious
@extend .error
border-width: 3px
CSS Output
.error, .error--serious {
border: 1px #f00;
background-color: #fdd;
}
.error--serious {
border-width: 3px;
}
当一个类扩展另一个类时,Sass 会对所有与扩展器匹配的元素进行样式设置,就像它们也与正在扩展的类匹配一样。当一个类选择器扩展另一个类选择器时,它的工作方式与将扩展类添加到 HTML 中已具有扩展类的每个元素一样。你可以只写 class="error--serious"
,Sass 会确保它的样式也像 class="error"
一样。
¥When one class extends another, Sass styles all elements that match the extender
as though they also match the class being extended. When one class selector
extends another, it works exactly as though you added the extended class to
every element in your HTML that already had the extending class. You can just
write class="error--serious"
, and Sass will make sure it’s styled as though it
had class="error"
as well.
当然,选择器不仅仅在样式规则中单独使用。Sass 知道要在使用选择器的任何地方进行扩展。这可确保你的元素的样式与扩展选择器完全匹配。
¥Of course, selectors aren’t just used on their own in style rules. Sass knows to extend everywhere the selector is used. This ensures that your elements are styled exactly as if they matched the extended selector.
SCSS Syntax
.error:hover {
background-color: #fee;
}
.error--serious {
@extend .error;
border-width: 3px;
}
Sass Syntax
.error:hover
background-color: #fee
.error--serious
@extend .error
border-width: 3px
CSS Output
.error:hover, .error--serious:hover {
background-color: #fee;
}
.error--serious {
border-width: 3px;
}
⚠️ Heads up!
扩展在样式表的其余部分编译后得到解决。特别是,它发生在 父选择器 解决之后。这意味着如果你 @extend .error
,它不会影响 .error { &__icon { ... } }
中的内部选择器。这也意味着 SassScript 中的父选择器 看不到 extend 的结果。
¥Extends are resolved after the rest of your stylesheet is compiled. In
particular, it happens after parent selectors are resolved. This means
that if you @extend .error
, it won’t affect the inner selector in .error { &__icon { ... } }
. It also means that parent selectors in SassScript
can’t see the results of extend.
工作原理工作原理 permalink
¥How It Works
与 混入 将样式复制到当前样式规则不同,@extend
更新包含扩展选择器的样式规则,以便它们也包含扩展选择器。在扩展选择器时,Sass 会进行智能统一:
¥Unlike mixins, which copy styles into the current style rule, @extend
updates style rules that contain the extended selector so that they contain the
extending selector as well. When extending selectors, Sass does intelligent
unification:
-
它永远不会生成像
#main#footer
这样不可能匹配任何元素的选择器。¥It never generates selectors like
#main#footer
that can’t possibly match any elements. -
它确保复杂的选择器是交错的,以便无论 HTML 元素以何种顺序嵌套,它们都可以工作。
¥It ensures that complex selectors are interleaved so that they work no matter which order the HTML elements are nested.
-
它尽可能地修剪冗余选择器,同时仍然确保特异性大于或等于扩展器的特异性。
¥It trims redundant selectors as much as possible, while still ensuring that the specificity is greater than or equal to that of the extender.
-
它知道一个选择器何时与另一个选择器匹配,并且可以将它们组合在一起。
¥It knows when one selector matches everything another does, and can combine them together.
-
¥It intelligently handles combinators, universal selectors, and pseudo-classes that contain selectors.
SCSS Syntax
.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 Syntax
.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 Output
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;
}
💡 Fun fact:
你可以使用 选择器功能 直接访问 Sass 的智能统一!selector.unify()
函数 返回一个与两个选择器的交集相匹配的选择器,而 selector.extend()
函数 的工作方式与 @extend
类似,但在单个选择器上。
¥You can directly access Sass’s intelligent unification using selector
functions! The selector.unify()
function returns a selector that
matches the intersection of two selectors, while the selector.extend()
function works just like @extend
, but on a single selector.
⚠️ Heads up!
由于 @extend
更新包含扩展选择器的样式规则,因此它们的样式在 级联 中的优先级取决于扩展选择器的样式规则出现的位置,而不是基于 @extend
出现的位置。这可能会令人困惑,但请记住:如果你将扩展类添加到 HTML,则这些规则的优先级相同!
¥Because @extend
updates style rules that contain the extended selector,
their styles have precedence in the cascade based on where the extended
selector’s style rules appear, not based on where the @extend
appears.
This can be confusing, but just remember: this is the same precedence those
rules would have if you added the extended class to your HTML!
占位符选择器占位符选择器 permalink
¥Placeholder Selectors
有时你想编写一个仅用于扩展的样式规则。在这种情况下,你可以使用 占位符选择器,它看起来像以 %
而不是 .
开头的类选择器。任何包含占位符的选择器都不包含在 CSS 输出中,但扩展它们的选择器包含在 CSS 输出中。
¥Sometimes you want to write a style rule that’s only intended to be extended.
In that case, you can use placeholder selectors, which look like class
selectors that start with %
instead of .
. Any selectors that include
placeholders aren’t included in the CSS output, but selectors that extend them are.
SCSS Syntax
.alert:hover, %strong-alert {
font-weight: bold;
}
%strong-alert:hover {
color: red;
}
CSS Output
.alert:hover {
font-weight: bold;
}
私有占位符私有占位符 permalink
¥Private Placeholders
与 模块成员 类似,占位符选择器可以通过其名称以 -
或 _
开头来标记为私有。私有占位符选择器只能在定义它的样式表中扩展。对于任何其他样式表,该选择器看起来就像不存在一样。
¥Like module members, a placeholder selector can be marked private by
starting its name with either -
or _
. A private placeholder selector can
only be extended within the stylesheet that defines it. To any other
stylesheets, it will look as though that selector doesn’t exist.
扩展范围扩展范围 permalink
¥Extension Scope
当一个样式表扩展选择器时,该扩展只会影响上游模块中编写的样式规则,即该样式表使用 @use
规则 或 @forward
规则 加载的模块、这些模块加载的模块等等。这有助于使你的 @extend
规则更具可预测性,确保它们仅影响你在编写规则时意识到的样式。
¥When one stylesheet extends a selector, that extension will only affect style
rules written in upstream modules—that is, modules that are loaded by that
stylesheet using the @use
rule or the @forward
rule, modules loaded
by those modules, and so on. This helps make your @extend
rules more
predictable, ensuring that they affect only the styles you were aware of when
you wrote them.
⚠️ Heads up!
如果你使用的是 @import
规则,则扩展根本不受范围限制。它们不仅会影响你导入的每个样式表,还会影响导入你的样式表的每个样式表以及这些样式表导入的其他所有内容,等等。如果没有 @use
,扩展就是全局的。
¥Extensions aren’t scoped at all if you’re using the @import
rule. Not
only will they affect every stylesheet you import, they’ll affect every
stylesheet that imports your stylesheet, everything else those stylesheets
import, and so on. Without @use
, extensions are global.
强制和可选扩展强制和可选扩展 permalink
¥Mandatory and Optional Extends
通常,如果 @extend
与样式表中的任何选择器都不匹配,Sass 将产生错误。这有助于防止拼写错误或重命名选择器而不重命名从它继承的选择器。要求扩展选择器存在的扩展是强制性的。
¥Normally, if an @extend
doesn’t match any selectors in the stylesheet, Sass
will produce an error. This helps protect from typos or from renaming a selector
without renaming the selectors that inherit from it. Extends that require that
the extended selector exists are mandatory.
但这可能并不总是你想要的。如果你希望 @extend
在扩展选择器不存在时不执行任何操作,只需将 !optional
添加到末尾即可。
¥This may not always be what you want, though. If you want the @extend
to do
nothing if the extended selector doesn’t exist, just add !optional
to the end.
扩展还是混合?扩展还是混合? permalink
¥Extends or Mixins?
Extends 和 混入 都是 Sass 中封装和重用样式的方式,这自然会引发何时使用哪一种的问题。当你需要使用 参数 配置样式时,Mixin 显然是必要的,但如果它们只是一大块样式怎么办?
¥Extends and mixins are both ways of encapsulating and re-using styles in Sass, which naturally raises the question of when to use which one. Mixins are obviously necessary when you need to configure the styles using arguments, but what if they’re just a chunk of styles?
根据经验,当你表达语义类(或其他语义选择器)之间的关系时,扩展是最佳选择。因为类 .error--serious
的元素是一个错误,所以它扩展 .error
是有意义的。但对于非语义样式集合,编写 mixin 可以避免级联问题,并使配置更容易。
¥As a rule of thumb, extends are the best option when you’re expressing a
relationship between semantic classes (or other semantic selectors). Because an
element with class .error--serious
is an error, it makes sense for it to
extend .error
. But for non-semantic collections of styles, writing a mixin can
avoid cascade headaches and make it easier to configure down the line.
💡 Fun fact:
大多数网络服务器使用一种非常擅长处理重复的相同文本块的算法来压缩它们所提供的 CSS。这意味着,尽管 mixin 可能会比扩展生成更多的 CSS,但它们可能不会大幅增加用户需要下载的 CSS 量。因此,请选择对你的用例最有意义的功能,而不是生成最少 CSS 的功能!
¥Most web servers compress the CSS they serve using an algorithm that’s very good at handling repeated chunks of identical text. This means that, although mixins may produce more CSS than extends, they probably won’t substantially increase the amount your users need to download. So choose the feature that makes the most sense for your use-case, not the one that generates the least CSS!
局限性局限性 permalink
¥Limitations
不允许的选择器不允许的选择器 permalink
¥Disallowed Selectors
- Dart Sass
- ✓
- LibSass
- ✗
- Ruby Sass
- ✗
LibSass 和 Ruby Sass 目前允许扩展像 .message.info
这样的复合选择器。然而,这种行为与 @extend
的定义不匹配:它不是像具有 class="message info"
那样对与扩展选择器匹配的元素进行样式设置(这会受到包含 .message
或 .info
的样式规则的影响),而是仅使用包含 .message
和 info
的规则对它们进行样式设置。
¥LibSass and Ruby Sass currently allow compound selectors like .message.info
to be extended. However, this behavior doesn’t match the definition of
@extend
: instead of styling elements that match the extending selector as
though it had class="message info"
, which would be affected by style rules
that included either .message
or .info
, it only styled them with rules
that included both .message
and info
.
为了保持 @extend
的定义简单易懂,并保持实现干净和高效,该行为现已弃用,并将从未来版本中删除。
¥In order to keep the definition of @extend
straightforward and
understandable, and to keep the implementation clean and efficient, that
behavior is now deprecated and will be removed from future versions.
详细信息请参见 重大变更页面。
¥See the breaking change page for more details.
只能扩展简单的选择器(例如 .info
或 a
等单独的选择器)。如果 .message.info
可以扩展,那么 @extend
的定义表明与扩展器匹配的元素将被设计为与 .message.info
匹配一样。这与同时匹配 .message
和 .info
是一样的,所以用 @extend .message, .info
来代替 .message
并没有任何好处。
¥Only simple selectors—individual selectors like .info
or a
—can be
extended. If .message.info
could be extended, the definition of @extend
says
that elements matching the extender would be styled as though they matched
.message.info
. That’s just the same as matching both .message
and .info
,
so there wouldn’t be any benefit in writing that instead of @extend .message, .info
.
类似地,如果 .main .info
可以扩展,它会做(几乎)与单独扩展 .info
相同的事情。细微的差异并不值得让人困惑,因为它看起来像是在做一些本质上不同的事情,所以这也是不允许的。
¥Similarly, if .main .info
could be extended, it would do (almost) the same
thing as extending .info
on its own. The subtle differences aren’t worth the
confusion of looking like it’s doing something substantially different, so this
isn’t allowed either.
SCSS Syntax
.alert {
@extend .message.info;
// ^^^^^^^^^^^^^
// Error: Write @extend .message, .info instead.
@extend .main .info;
// ^^^^^^^^^^^
// Error: write @extend .info instead.
}
Sass Syntax
.alert
@extend .message.info
// ^^^^^^^^^^^^^
// Error: Write @extend .message, .info instead.
@extend .main .info
// ^^^^^^^^^^^
// Error: write @extend .info instead.
HTML 启发式HTML 启发式 permalink
¥HTML Heuristics
当 @extend
交错复杂的选择器 时,它不会生成祖级选择器的所有可能组合。它可能生成的许多选择器不太可能与真实的 HTML 实际匹配,并且生成所有这些选择器会使样式表太大而几乎没有实际价值。相反,它使用 启发式:它假设每个选择器的祖级都是独立的,不会与任何其他选择器的祖级交错。
¥When @extend
interleaves complex selectors, it doesn’t generate all
possible combinations of ancestor selectors. Many of the selectors it could
generate are unlikely to actually match real HTML, and generating them all would
make stylesheets way too big for very little real value. Instead, it uses a
heuristic: it assumes that each selector’s ancestors will be self-contained,
without being interleaved with any other selector’s ancestors.
SCSS Syntax
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 Syntax
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 Output
header .warning li, header .warning aside .notice dd, aside .notice header .warning dd {
font-weight: bold;
}
在 @media
中扩展在 @media 中扩展 permalink
¥Extend in @media
虽然 @media
和其他 CSS at 规则 中允许使用 @extend
,但不允许扩展出现在其 at 规则之外的选择器。这是因为扩展选择器仅适用于给定的媒体上下文,并且无法确保在生成的选择器中保留限制而不复制整个样式规则。
¥While @extend
is allowed within @media
and other CSS at-rules, it’s not
allowed to extend selectors that appear outside its at-rule. This is because the
extending selector only applies within the given media context, and there’s no
way to make sure that restriction is preserved in the generated selector without
duplicating the entire style rule.
SCSS Syntax
@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 Syntax
@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