三种最流行的 CSS 预处理器都支持 **extend**。我没有数据,但根据我的经验,这个功能一开始会让人感到困惑,因此使用率不高。我认为我们可以稍微讨论一下它是如何工作的,为什么要使用它以及何时使用它,一些需要注意的地方,以及它在未来可能是什么样子。
基本概念
继承。**我希望这个选择器继承自这个选择器。**
基本用法
我将在这里使用 Sass。我们稍后将介绍所有预处理器。
.foo {
color: red;
}
.bar {
@extend .foo;
}
输出
.foo, .bar {
color: red;
}
请注意,它不会“获取”来自 .foo
的样式并将其插入到 .bar
下方,从而复制样式。相反,它会用逗号分隔原始选择器,更有效地将样式应用于两者。
与 Mixin 的关系
您通常可以使用 mixin 来实现相同的结果。
@mixin stuff {
color: red;
}
.foo {
@include stuff;
}
.bar {
@include stuff;
}
这会将这些样式引入这两个选择器。这通常更容易理解,并且实际上“需要注意的地方”更少,因此更常见。请注意,输出将样式移动到这两个位置。
.foo {
color: red;
}
.bar {
color: red;
}
虽然这并不是什么大问题(特别是由于重复文本特别容易被 gzip 压缩),但它效率较低。
但是,Mixin 可以做 Extend 做不到的事情,即接收参数并在输出中处理/使用它们。
@mixin padMasterJ ($param: 10px) {
padding: $param;
}
Extend 做不到这一点,因此**经验法则是**:任何时候你使用没有参数的 mixin,extend 都会更有效率。当然,每个规则都有例外,我们稍后会讨论。
需要注意的另一点是,所有顶级选择器都是可扩展的。LESS 用户会更本能地理解这一点,因为在 LESS 中,所有选择器也都是 mixin,这对于 Sass 用户来说很奇怪。
Sass 的方式
到目前为止,我们一直在使用 Sass,但让我们注意一下有关 Sass 如何处理 Extend 的一些具体内容。它还会扩展所有嵌套选择器。
.module {
padding: 10px;
h3 {
color: red;
}
}
.news {
@extend .module;
}
输出
.module, .news {
padding: 10px;
}
.module h3, .news h3 {
color: red;
}
Sass extend 的一个限制是它不能扩展嵌套选择器。以下是一个示例
.header {
h3 {
color: red;
}
}
.special-header {
/* Error: can't extend nested selectors */
@extend .header h3;
}
LESS 的方式
LESS 使用 &:extend
。要移植我们的超简单示例,它将如下所示
.foo {
color: red;
}
.bar {
&:extend(.foo);
}
输出
.foo,
.bar {
color: red;
}
LESS extend 的一个值得注意的地方是它默认不扩展嵌套选择器。因此,如果我们这样做
.module {
padding: 10px;
h3 {
color: red;
}
}
.news {
&:extend(.module);
}
h3 不会被扩展,并且将输出
.module,
.news {
padding: 10px;
}
.module h3 {
color: red;
}
但是,如果您添加 all
关键字,例如
.news {
&:extend(.module all);
}
它将扩展所有内容。
.module,
.news {
padding: 10px;
}
.module h3,
.news h3 {
color: red;
}
您还可以指定一个嵌套选择器来代替 all
关键字进行扩展。
Stylus 的方式
Stylus 使用 @extend
,并且其工作方式与 Sass 非常相似。
.module
padding 10px
h3
color red
.news
@extend .module
输出
.module,
.news {
padding: 10px;
}
.module h3,
.news h3 {
color: #f00;
}
与 Sass 的一个显著区别在于它可以扩展嵌套选择器。例如
.header
padding 10px
h3
color red
.special-header
@extend .header h3
将为您提供
.header {
padding: 10px;
}
.header h3,
.special-header {
color: #f00;
}
扩展占位符
Sass 和 Stylus 都有占位符选择器。在 Sass 中
%grid-1-2 {
float: left;
width: 50%;
}
在 Stylus 中
$grid-1-2
float left
width 50%
这是一件非常小的事情。它只是不在 CSS 中输出占位符(因此称为“占位符”),它只是允许您扩展它们。**这里最大的优势在于您可以使用不会影响外部命名方案的内部命名方案。** 网格类就是一个很好的例子。像“grid-1-2”和“grid-1-3”这样的名称在内部是很好的名称,但不是很好的实际 HTML 类名。使用占位符,您可以将它们保留在内部。
.main-content {
@extend %grid-2-3;
}
.sidebar {
@extend %grid-1-3;
}
注意选择器顺序
由于选择器重写,您可能会偶尔遇到选择器比另一个覆盖它的选择器更早重写的情况。请注意,特异性永远不会改变,但是当两个选择器的特异性完全相同,最终 CSS 中较下面的那个“获胜”。
例如
.one {
color: red;
}
.two {
color: green;
}
.three {
@extend .one;
}
然后你有一个这样的元素
<div class="three two">test</div>
您可能会假设类名 three“获胜”,因为它在 Sass 中位于最下方,扩展了 .one
,因此颜色将为红色。但是 .three
实际上在 .two
之上被重写了。
.one, .three {
color: red;
}
.two {
color: green;
}
由于该元素还具有类名 .two
,因此最终呈现的颜色实际上是绿色。
注意超大输出
这是一个非常合理的场景,Nicole Sullivan 已经介绍过
- 您为 clearfix 创建了一个占位符类
- 您创建了一个扩展 clearfix 的通用模块类
- 您创建了其他扩展模块的模块类(“链式”扩展)
%clearfix {
&:before,
&:after {
content: " ";
display: table;
}
&:after {
clear: both;
}
}
.module {
padding: 10px;
@extend %clearfix;
h3 {
color: red;
@extend %clearfix;
span {
float: right;
}
}
}
.sports {
@extend .module;
}
.news {
@extend .module;
}
仅 clearfix 的输出开始看起来有点厚重
.module:before, .sports:before,
.news:before, .module h3:before,
.sports h3:before, .news h3:before,
.module:after, .sports:after,
.news:after, .module h3:after,
.sports h3:after, .news h3:after {
content: " ";
display: table;
}
.module:after, .sports:after,
.news:after, .module h3:after,
.sports h3:after, .news h3:after {
clear: both;
}
这仅仅是几个类。我相信一个完整的项目可能会变得更加复杂。这并不是说它不会工作,只是您可能更适合使用一个实际的类并根据需要将其放入 HTML 中。或者在某些情况下,mixin 甚至可能生成更少的代码,具体取决于嵌套的深度。
经验法则是:选择需要最少最终输出的技术(或最适合您的技术)。
注意媒体查询
没有任何语言允许您从媒体查询内部扩展到在媒体查询外部定义的选择器。
这是可以的
@media (max-width: 100px) {
.module {
padding: 10px;
}
.news {
@extend .module;
}
}
这将无法工作
.module {
padding: 10px;
}
@media (max-width: 100px) {
.news {
@extend .module;
}
}
这是选择器移动/重写的本质。如果您将该选择器移出查询,则不再尊重作者的意图(它将无论媒体查询如何而应用)。也许可以将规则复制到内部,但这不符合 extend 的意图。Sass 3.3 将提供一种处理方法,但真正的答案是原生浏览器支持。
Extend 的未来
Extend 在预处理器层非常强大,但在浏览器级别将会更加强大。不再需要逗号分隔的选择器、超大的输出、源代码顺序问题、媒体查询问题……所有这些都不会存在。浏览器只需要知道将另一个选择器的规则应用到这个选择器上即可。据我所知,相关负责人需要一些说服才能相信这是一个好主意,但现在他们已经相信了,接下来就是编写所有规范等等。但我没有可以链接的证据来证明这一点。
也许它可以是
.module {
padding: 10px;
}
.news {
!extend .module;
}
我不确定……我怀疑无法使用 @extend,因为这些 @ 符号已经有了一些特殊的含义(它们以条件方式包装代码块)。并且像 extend() 这样的函数通常用于值而不是属性。对于原生语言来说,这有点新颖/奇怪。
我很乐意听到你对 extend 的所有想法。如果你使用它,你是如何使用它的,你发现了什么其他问题,你如何想象它在原生环境中工作等等。
我认为 @extend 有时太乱了,但这只是我个人观点。也许我需要更多地使用它,但这是我目前的看法。
我不建议在类上使用 extend……它有时会让人非常困惑。
我发现它们非常有用,并且它们可以真正减少 CSS 代码膨胀(与复制样式相比)。坚持使用它。
Stephen,帮我更多地了解使用 extend 在类上会造成哪些困惑?
同意!!
我同意 Stephen 的观点。如果你只扩展静默类(占位符),混淆就会减少。知道何时扩展 .(类)、#(ID)或 %(静默类)可能会很麻烦。这允许你创建仅包含可扩展静默类的文件,这些文件可以导入到任何地方并继续你的编码。
我最近不幸遇到一个项目,我无法访问 HTML。每个页面都对元素使用页面唯一的 ID(即使这些元素在多个页面之间共享)。
@extend 帮助我保持了理智,因为我可以创建可重用的基础类(使用我自己的命名约定)并在多个 ID 中扩展它们,例如
.menu 扩展到 #page1menu、#page2menu、#page3menu 等
,同时仍然保持我的 CSS 相对有序。那个 HTML 真是太乱了……我热爱 @extend。我创建了一个 Sass 文件,其中包含几乎所有基本属性和值。所以,如果我想让某个东西左浮动,我只需使用 @extend %float-left。我认为它通过将选择器组合到输出的 CSS 中来保持一切整洁。
如何在媒体查询中使用占位符扩展元素?你有没有找到什么很酷的方法来做到这一点?
据我所知,目前还不行。
尽管你可以在占位符本身内部应用媒体查询样式,例如
很棒的概述,Chris。我一段时间前开始在适用的地方实现 @extends,它们是一个很棒的“工具”,尤其是在使用占位符选择器时。
到目前为止,对我来说最有效的方法是创建一个名为 _patterns.scss 的文件,其中包含大量占位符选择器。我发现自己有很多可重用的“模式”,我可以在所有样式表中使用 @extend——在代码可读性方面是一个很大的改进。
@extend 非常混乱,它会累积大量选择器,这使得输出比你想象的要大得多,并且也会增加选择器数量。如果你在大型项目中使用 @extend,那么就别指望 IE 了,因为它对选择器数量有限制。
我们最近删除了所有 @extend 并手动添加了类。建议谨慎使用 @extend。
我也遇到了超过 IE 选择器限制的问题,但是 BlessCSS 解决了这个问题。你可以寻找其他分割样式表的方法,但在我的项目中 Bless 效果最好。
@extend 在处理一些分散的代码时可以成为救星。我使用 Typekit 并注意到页面渲染时存在延迟,因为字体等待下载。我最终切换到异步加载字体,同时利用 Typekit 在加载时动态添加到 html 标签的 .wf-loading、.wf-inactive 和 .wf-active 类来消除 FOUT。
我想分享一个关于我如何以这种方式使用 extends 的 SASS 提示(也适用于 Google Web Font API),并提前为这条评论的冗长表示歉意。
有像这样的占位符,我将所有自定义排版都扩展到
我遇到的一个障碍是无法直接在代码中扩展它们。SASS 遇到了一些父级引用问题,并弄乱了类顺序(将子级放在父级之前并重复)。我实际上不得不使用执行 extends 的 mixin,例如
例如,在 h1、h2 标题上使用上述 @mixin 将输出以下内容
我知道上面的代码看起来过于复杂,可能还有更好的方法,但我找不到替代解决方案,所以就这样吧。
SASS 中 @extend 的另一个巧妙之处在于它能够在另一个 @extend 将样式级联足够时忽略不必要的 extends。它完全跳过了它。我最初以为是 bug,结果只是 SASS 的静默效率。
感谢 Chris 又带来了一篇好文章。
可以看出它很快就会变得很复杂。
到目前为止,我和我工作中的同事出于这个原因一直坚持使用普通的 CSS。至少它很容易理解;)
有趣的概述。我倾向于谨慎使用 @extend,几乎只在它能节省一些文件大小的时候使用。关于网格,我更喜欢使用带参数的 mixin 而不是 @extend,这样我就不必为每个变体创建数百万个网格类/占位符了 :P
我一直在使用 @extend。对我来说,它比 mixin 更好。
如何将变量传递给
@extend
?两者用于不同的目的,因此无法真正比较。如果我传入变量,我会使用 mixin,如果我使用占位符,我会使用 extend,我通常不会扩展类。经常使用 extend。主要使用占位符,偶尔扩展类。当我学习 Sass 时,我几乎立即开始使用它。
在 LESS 中不是可以这样做吗?
这会复制
.red
的样式到.alert
你的代码编译为
使用 extend
我最近遇到了 SASS(Windows 上的 Compass)的一个奇怪行为,它使得几乎不可能 @extend %placeholder 选择器。
考虑以下设置
我有一个文件 _variables.scss,其中包含变量和一些几乎到处都使用的 mixin。我在那里放置了占位符选择器
%clearfix
。在其他一些文件中,我
@import
了这个文件并@extend
了%clearfix
。结果:CSS 包含多次相同输出的副本。似乎 Compass 正在链接 _variables.scss 中的选择器,并在
@import
_variables.scss 的任何地方打印它。之前有人注意到过吗?或者有人知道如何避免这个问题吗?
在使用 @extend 时要谨慎,尤其是在已经使用 Bootstrap 或类似会使代码库膨胀的东西时。如果你不小心,很可能比你预期的更早地遇到恼人的 IE 4096 选择器限制。
更多信息:http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
Chris:感谢你写了这篇文章。我喜欢能够学习一些东西(在本例中是扩展占位符选择器),并能够立即在工作中使用它。
一个可能的更正:我认为“扩展占位符”部分中的第三个代码片段应该是
确实,谢谢。
对我来说,extend的缺点很容易超过其优点。
我计划避免使用它。
如果你知道你对它输出为“.foo, .bar”没问题,为什么不自己这样做呢?extend不比那样更冗长、更难理解吗?
你可以在纯CSS中通过两种方式实现这一点。
在你的样式表中
或在你的标记中
另外,我并没有错过这篇帖子使用extend作为另一种实现方式的重点。只是想指出这一点。
没关系,我刚刚看到你发布了这个。我真是个笨蛋。
好吧,感谢Chris撰写了另一篇很棒的文章,但就目前而言,我对@extend感到非常困惑,我正在等待更多信息,然后才能在我的网站中开始使用@extend。
Extend很有用,但这在我看来很疯狂。为什么HTML中的类名规则和“内部”规则不同!?
因为HTML中的类**始终存在**。仅用于内部的占位符类可以在内部有条件地使用,而无需笨拙的永久命名。想象一下,如果我们可以这样做
现在HTML中唯一的类是“sidebar”,这可能很有意义,我们通过同样有意义的内部名称来控制其布局。
你的.grid-x-x类之间唯一的区别可能是它们的宽度,你也可以扩展到共同点并避免重复,然后如果你想更改边距或其他任何内容(在你的公共网格类上),你可以在一个地方进行更改
除了“良好的命名”之外,与直接将(你将扩展的)类添加到div相比,有什么优势吗?
抱歉,忘记刷新页面了。没有最新的评论。
很高兴能够深入了解实践中的情况。
很棒的文章,但我有一个在不同文件中
@extend
类的问题。我正在使用Prepros编译我的Sass项目。所以我在button.scss文件中声明了
.btn-primary
,该文件被导入到名为project.scss的主文件中(使用@import
)。我想在另一个名为home.scss的文件中@extend
该类。Prepros日志告诉我:无法@extend“.btn-primary”
我想知道我们是否可以使用SASS在不同文件中
@extend
选择器。干杯
你解决这个问题了吗?我遇到了类似的问题
我最近开始在我的工作项目中使用sass和@extend。我为其中一位客户做的工作要求我们在每个项目中为相同预定义的html编写css,所以即使项目看起来完全不同,它的大部分情况下始终具有相同的html。能够创建一次样式,然后在特定的html代码块上使用@extend来获得我想要的样式,这很好。它允许在通常会使代码重用极其困难的环境中进行代码重用。