Sass 的 & 符号

Avatar of Richard Finelli
Richard Finelli

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

& 是 Sass(以及 Less)中一个非常有用的功能。它用于嵌套。当您知道如何使用它时,它可以是一个很好的节省时间的工具,但当您正在努力学习时,它也可能是一个浪费时间的工具,因为您本可以使用普通的 CSS 写出相同的代码。

让我们看看是否真的能理解它。

基本嵌套

.parent {
  .child {}
}

这将编译为

.parent .child {}

您可以根据需要进行更深层的嵌套,但最好只嵌套一到两层,以防止过于具体的选择器(这将减少实用性,并使覆盖变得更加困难)。

添加另一个类

当您进行嵌套并希望创建更具体的选择器(例如同时具有两个类的元素)时,& 会非常有用,例如

.some-class.another-class { }

您可以在嵌套时使用 & 来实现这一点。

.some-class {
  &.another-class {}
}

& 始终引用嵌套时的父选择器。将 & 视为被移除并替换为父选择器。例如:

顿悟时刻!

以下 Sass 代码

.parent {
  .child {}
}

实际上可以看作是使用 & 进行嵌套的简写方式

.parent {
  & .child {}
}

因此,这两个示例都将编译为相同的结果

.parent .child {}

使用 & 的示例与不使用 & 的示例没有什么不同。不使用 & 进行嵌套是使用 & 进行嵌套的简写形式。我们可以将 & 视为一种机制,它允许我们在子选择器中放置父选择器,无论我们将其放置在何处。它允许我们在嵌套时进行修改。让我们看一些更多的例子。

使用 & 与伪类

您可以使用 & 以更简洁的方式在类上编写伪类

.button {
  &:visited { }
  &:hover { }
  &:active { }
}

这将编译为

.button:visited { }
.button:hover { }
.button:active { }

在这种情况下,& 允许我们在编写代码时将 .button 直接放置在伪类旁边,而无需重复。如果我们从该示例中省略 &,基本嵌套将在它们之间添加空格,如下所示...

.button :hover

...这并不相同。

使用 & 与 >、+ 和 ~

使用 & 组合器 >相邻兄弟 组合器 +一般兄弟 组合器 ~ 非常容易。最初我以为您必须使用 &,但

// You don't actually have to do this.
// Here, the ampersand is implied.
.button {
  & > span { }
  & + span { }
  & ~ span { }
}

省略选择器中的 & 也可以在这里使用

// This compiles the same as above
.button {
  > span { }
  + span { }
  ~ span { }
}

这两个示例都将编译为以下 CSS

.button > span { }
.button + span { }
.button ~ span { }

基于上下文进行限定

嵌套选择器并不一定需要以 & 开始。您可以通过将 & 放在右侧来限定选择器。

.button {
  body.page-about & { }
}

我们正在将父选择器重新定位到我们需要的位置。这对基于不同父级进行选择器限定非常有用。这将编译为

body.page-about .button {}

意思是,仅当 button 类是具有 page-about 类的 body 的子元素时,才选择它。

调整 & 的定义

& 视为不仅会被父选择器替换,还会被*编译后的*父选择器替换。

当嵌套深度超过两层时,这一点很重要,因为多个层级中都包含 &。以下两个古怪的例子说明了这一点。

古怪但有效的示例 #1

不要编写看起来像这样的选择器

.parent {
  .child {
    & div & & > a {}
  }
}

对于每个 &,它都将被编译后的父选择器替换。因此,每次出现 & 时,我们都会插入 .parent .child。以下是编译后的 CSS

.parent .child div .parent .child .parent .child > a {}

古怪但有效的示例 #2

.parent {
  .child {
    .grand-child & {
      &.sibling { }
    }
  }
}

要从心理上编译此 CSS 代码,请从最顶层开始,逐层向下剥离,将 & 替换为新的编译后的父选择器。

以下是编译后的结果

.grand-child .parent .child.sibling {}

& 不是什么

我发现自己经常将 & 用于它本来不适用的地方。& 无法让您选择性地向上遍历嵌套的选择器树,到达特定位置,然后仅使用您希望使用的编译后的父选择器的一部分。我之前希望能够执行类似的操作

.grand-parent {
  .parent {
    &(1) .child {} // Trying to get `.parent`, not `.grand-parent .parent`
  }
}

我的本意是让 & 仅被 .parent 替换,以期编译为以下结果

.parent .child {}

但这行不通。

& 始终是完全编译后的父选择器。

@at-root 来帮忙

值得一提的是,@at-root 允许您完全跳出嵌套结构,到达嵌套树的“根”。

.grand-parent {
  .parent {
    @at-root .child {}
  }
}

我们已经从嵌套树中传送到了以下编译后的 CSS

.child {}

这很好。从组织的角度来看,所有代码仍然分组在一起,这可以被视为嵌套的一个不为人知的优势。@at-root 可以帮助保持较低的特异性级别,因为您不再拥有编译后的父选择器来提高特异性。同时,仍然保持代码在概念上的组织,并使用嵌套

.grand-parent {
  .parent {
    @at-root body.page-about .child {}
  }
}

编译后的 CSS

body.page-about .child {}

& 还有其他一些有趣的用例。

提高特异性

有时你需要降低第三方 CSS 库的特异性以获取样式的控制权。

.parent.parent {}

这比使用 ID、内联样式或 !important 要温和得多,而且它可能比使用任意父元素限定选择器更有利。特异性级别不会根据选择器的上下文提升,而是只根据它本身提升。使用 & 你可以像这样做到这一点。

.parent {
  &#{&} { }
}

#{ } 中的 插值括号 是必需的,因为两个相邻的 & 符号在 Sass 中是无效的。

修改 & 符号

即使你不能在没有插值括号的情况下让两个 & 符号相邻(正如我们之前在伪类示例中演示的那样),你仍然可以让另一个选择器与 & 符号相邻。与 & 符号相邻对于修改器类非常有用。

.btn {
  &-primary {}
  &-secondary {}
}

编译后的 CSS

.btn-primary {}
.btn-secondary {}

如果使用命名方法(例如 BEM),它使用连字符和下划线组合的类而不是组合选择器,这将非常有用。

结论

与嵌套结合使用的 & 符号是一个很棒的功能。一旦你了解了它的作用,编写 Sass 就会变得更容易、更快、更不容易出错。

以下是一些关于 & 符号的专门文章,供你参考。