不要在 sass 中使用嵌套

sass 中有一个被很容易被开发者滥用的功能,就是可以嵌套选择器,我之前的项目就是滥 用如此,导致维护起来臭不可闻, 在新项目开始之际,我读到这篇文章, 解决了我的一些疑 问,特此记录一下。

什么是选择器嵌套

要解决选择器过度嵌套的问题,首先要知道什么是嵌套选择器,比如:

.parent {
color: red;

.child {
color: blue;
}
}

编译后的 css 如下:

.parent {
color: red;
}


.parent .child {
color: blue;
}

初看起来这很方便,在代码量不大的情况下也很直观,那么这种写法有什么问题呢。

遇到的问题

我们先来看看 这里的一个例子 :

.tabs {
.tab {
background: red;

&:hover {
background: white;
}

.tab-link {
color: white;

@at-root #{selector-replace(&, '.tab', '.tab:hover')} {
color: red;
}
}
}
}

或者这个例子

.root {
width: 400px;
margin: 0 auto;

.links {
.link {
display: inline-block;

& ~ .link {
margin-left: 10px;
}

a {
padding: 10px 40px;
cursor: pointer;
background: gray;

&:hover {
background: blue;
color: white;
font-size: 700;
}

.icon {
margin-right: 5px;
@include selector-modifier(-2 ':hover', 1 suffix '.zh') {
color: red;
background: green;
}
@include selector-modifier(-2 ':hover', 1 suffix '.en') {
color: yellow;
background: green;
}
}
}
}
}
}

当嵌套达到 3 层甚至 3 层以上时,代码已经一团糟了, 你可能需要好一会儿才能明白这 这些代码究竟是什么意思 – 因为这确实有点复杂。

会产生歧义

最简单的情况,使用 & 指代父级元素:

/* SCSS */
.element {
&:hover {
color: red;
}
}

/* CSS */
.element:hover {
color: red;
}

但是你很可能不小心写错成这些:

 /* SCSS */
.element {
&hover {
color: red;
}
}

/* CSS */
.elementhover {
color: red;
}

/* *********************************** */
/* SCSS */
.element {
& .hover {
color: red;
}
}

/* CSS */
.element .hover {
color: red;
}

/* *********************************** */
/* SCSS */
.element {
&-hover {
color: red;
}
}

/* CSS */
.element-hover {
color: red;
}

/* *********************************** */
/* SCSS */
.element {
&.hover {
color: red;
}
}

/* CSS */
.element.hover {
color: red;
}

/* *********************************** */
/* SCSS */
.element {
.hover& {
color: red;
}
}

/* Syntax Error */
Invalid CSS after ".hover": expected "{", was "&"

"&" may only be used at the beginning of a compound selector.

/* *********************************** */
/* SCSS */
.element {
&:hover & {
color: red;
}
}

/* CSS */
.element:hover .element {
color: red;
}

/* *********************************** */
/* SCSS */
.element {
&:hover {
& {
color: red;
}
}
}

/* CSS */
.element:hover {
color: red;
}

同时如果你使用 BEM 之类的命名法,有可能还会导致某些选择器无法选中的问题。

嵌套选择器的合适场景

你可能已经猜到了, hover, focus, 以及伪选择器是潜逃选择的最佳实践, 比如:

.element {
/* Some CSS declarations */

&:hover,
&:focus {
/* More CSS declarations for hover/focus state */
}

&::before {
/* Some CSS declarations for before pseudo-element */
}
}

另一种情况是你需要基于组件设置一些特定的样式,比如: Modernizr 的 css 回退机 制写法:

.element {
/* Some CSS declarations */

.no-csstransforms & {
/* Some CSS declarations when CSS transforms are not supported */
}
}

推荐的解决办法

看代码,闭上眼静静感受吧。

.tabs {
overflow: hidden;

.tab {
background: red;

&:hover {
background: white;
}

.tab-link {
color: white;

@at-root #{selector-replace(&, '.tab', '.tab:hover')} {
color: red;
}
}
}
}

可以改写如下:

.tabs {
overflow: hidden;
}

.tab {
background: red;

&:hover {
background: white;
}
}

.tab-link {
color: white;

.tab:hover & {
color: red;
}
}

注意这个 .tab-link 的写法,很巧妙。

.tab-link {
color: white;

.tab:hover & {
color: red
;

}

}

总结

除了上面提到的适合嵌套选择的情况,你应该尽量避免使用它, 它造成的各种麻烦要比 表面上的方便多的多。