Extend 概念

Avatar of Chris Coyier
Chris Coyier 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

三种最流行的 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 已经介绍过

  1. 您为 clearfix 创建了一个占位符类
  2. 您创建了一个扩展 clearfix 的通用模块类
  3. 您创建了其他扩展模块的模块类(“链式”扩展)
%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 的所有想法。如果你使用它,你是如何使用它的,你发现了什么其他问题,你如何想象它在原生环境中工作等等。