在SASS/SCSS中过度嵌套选择器实际上有多糟糕?

22

我有一个.scss文件,其中包含以下内容:

nav {
  font-size: 0;
  ul {
    margin: $padding/3;
  }
  li {
    z-index: 10;
    position: relative;
    display: inline-block;
    font-size: $fontSize;
    /**
     * If we want separated, Uncomment!

    margin: $padding/3;
    @include border-radius(5px);

    */
    &:first-child {
      @include border-radius(0 5px 5px 0);
    }
    &:last-child {
      @include border-radius(5px 0 0 5px);
    }
    padding: $padding/3 0;
    @include background(linear-gradient(lighten($textColor, 10%), $textColor));
    border: 1px solid lighten($textColor, 20%);
    a {
      color: $brightColor;
      padding: $padding/3 $padding;
      font-weight: bold;
      text-decoration: none;
      @include transition(.2s all);

    }
    //Nested menues
    ul {
      opacity: 0;
      //display: none;
      position: absolute;
      margin: 0;
      top: 0;
      left: 0;
      right: 0;
      z-index: 5;
      pointer-events: none;
      @include transition(.2s all);
      li {
        @include background(linear-gradient(darken($brightColor, 10%), darken($brightColor, 30%)));
        display: block;
        border: 1px solid lighten($textColor, 20%);
        &:first-child {
          @include border-radius(0);
        }
        &:last-child {
          @include border-radius(0 0 5px 5px);
        }
        a {
          color: $textColor;
        }
      }
    }
    &:hover ul {
      pointer-events: all;
      top: 100%;
      opacity: 1;
      //display: block;
    }
  }
}

在实践中它有多糟糕/有害?我听说过很多关于“不要超过3个嵌套选择器!”的讨论。但它到底有多有害呢?它是否对页面加载产生任何明显影响?我做的基准测试表明没有,但我是否遗漏了什么?


1
我更担心他们生成的CSS文件的大小,但我相信编译器知道如何进行代码压缩... - BoltClock
能够得到一个正确的答案绝对是非常棒的。是否有一些编译器优化在幕后进行?当特定的SCSS规则(选择器?)被转换为可能表现不佳的内容时,是否可能从编译器接收警告? - ZenMaster
@ZenMaster - 我不确定警告,但我知道正在进行优化(虽然这可能是Compass的事情)。我见过许多情况下,具有匹配属性的选择器被分组在一起(因此类似p{颜色:红色} div{颜色:红色}的东西变成了p,div{颜色:红色})。 - Shauna
6个回答

36
取决于页面加载后DOM和样式的动态操作有多少。问题不在于页面加载(主要是)或初始布局中的缓慢选择器,而在于重绘/回流。
现在,Steve Souders表示对于普通网站,这根本不是真正的问题。 但是,在Web应用程序或高度交互式网站上,性能差的CSS规则可能会使您的重绘比必须慢。 如果您有很多次重绘...

专家们,如Nicole SullivanPaul IrishSteve Souders,已经讨论了CSS与JavaScript的交互方式以及如何编写高性能的CSS选择器。这不仅仅是深度问题(不同的选择器有不同的性能),但一个好的经验法则是限制深度和复杂性,以避免自己陷入麻烦 - 但不要太担心性能问题,请继续阅读。

然而,正如jankfree.org所指出的那样,并不是后代或特定选择器,而是在某些情境下特定属性(html5rocks.com)使绘制变得昂贵。我认为长或复杂的选择器更多地涉及到可维护性问题(尼古拉斯·加拉格尔),而非性能问题——需要记住可维护性会影响性能。高度可维护的代码可以更快地迭代,并且更容易调试(帮助您找到并修复性能问题)。

现在,关于Sass的优化问题。是的,Sass可以优化你的CSS。但它不能优化你的选择器。一个四级嵌套块将被输出为一个四级嵌套选择器。Sass不能改变它,否则可能会导致你的CSS无法正常工作。你作为作者,必须优化你编写Sass的方式来优化你的输出。我个人只在有限的情况下使用嵌套(对我来说,Sass的杀手功能是使用@extend和占位符来组合样式)。然而,如果你真的喜欢嵌套,你也许可以使用Sass父选择器引用(或者较新的@at-root)来调整你的输出。

据我所知,无论是Sass还是Compass都没有内置工具来分析选择器并警告它们。可能可以创建一个工具来做到这一点(设置最大深度并让您的预处理器警告您),利用AST。更直接地,Google Page Speed有一个现有的功能提供一些信息。SCSS Lint有一个嵌套选项。还有CSS Lint。(如果您还没有使用类似Grunt或Gulp的东西),理论上可以将它们添加到运行在您的Compass配置的on_stylesheet_saved中。

10

只需考虑如何编写实际的CSS选择器,不要仅因为元素是子元素就将所有内容嵌套。

nav li ul li a { 
    /* over specific, confusing */
}
.sub-menu a {
    /* add a class to nested menus */
}

一旦你开始链接那么多选择器,覆盖它们会变得困难,并且可能会导致特异性问题。


9
不要嵌套CSS。我们感觉很舒适地嵌套CSS,因为那样紧密地反映了我们在HTML中所做的事情。嵌套给了我们一个上下文,即“.some-child”在“.some-parent”之内。它使我们对级联具有一定的控制。但其他方面并没有太多作用。
正如SMACSS所建议的那样,我会在类名中进行嵌套。例如,使用“.child-of-parent”而不是“.parent .child”或“.parent > .child”。
实践中错误的嵌套可能导致页面极慢。请看GitHub如何加速其差异页面。最起码应该遵循内部规则,即不应该嵌套超过4层。
然而,我会更进一步地说,我们根本不应该嵌套CSS。我写了一篇博客文章阐述我的观点。希望这有用。

非常好的回答。谢谢你。我也会阅读你的博客文章 :) - Madara's Ghost

4

我的观点:

你告诉哪个对你的眼睛更糟糕

来自原始帖子

nav li ul li a {color: $textColor;}

或者如之前所建议的一样

.nav-menuitem-menu-menuitem-link {color: $textColor;}

所以...

问题是,“在SCSS(或SASS)中嵌套是否是不好的实践?”我认为不是。但这只是一个附带的论点。

更糟糕的做法是将SASS(或SCSS)输出以机器驱动状态保留在生产环境中。

S*SS只是您工具箱中的一种工具,与Notepad++、Git或Chrome没有什么不同。它的作用是通过将一些非常通用的编程概念引入构建某些css的过程中,使您的生活变得更轻松。 它的作用不是构建您的css。您不能指望它为您完成工作并创建完全可用、可读、高性能的输出。

尽可能多地嵌套,然后遵循良好的实践...

...这意味着在处理超嵌套输出之后手动微调您的css。使用您的超嵌套输出进行测试、构建等操作。当S*SS创建了我上面的第一个示例时,给该锚点添加一个类,并使用nav .class调用它。


1
我喜欢同时使用S@SS:P感谢您的回答,我同意这里说的很多,但是当涉及到动画和页面重新布局时,似乎存在过度嵌套的问题。因此,“神话”本身具有一定的价值。尽管如此,您得到了我的+1 :) - Madara's Ghost
@MadaraUchiha 嗯,如果你发现.if .your .nested div #is .too-deep导致网站无法正常运行,并且怀疑这是问题所在,那么不要犹豫,立即在S*SS(或者是S@SS?)中将其分离出来,并给它一个更短的选择器。 - monsto

4
仅仅想加入并强调其他人的说法。这种做法不是从性能角度来看不好(也许你会通过删除模糊/阴影和圆角比优化选择器获得更好的绘制时间增加),而是从可维护性的角度来看不好。
选择器嵌套得越深,结果CSS规则就越具体(你已经知道了)。因此,当你想要在某个时候“压制”该规则时,你将不得不编写一个相等(或更大)特定性的规则,以进一步覆盖第一个规则。如果你在其中有一个ID,那么它会使它变得更加具体(因此除非你需要它们并且知道你不需要在下一行覆盖它们,否则请避免使用)。
为了推到其逻辑结论,除非你需要,否则不要嵌套。不要像这样设置规则:
.selector .another .yeah-another {}
当这样做同样可以实现:
.yeah-another {}
这只会使每个人(包括你)的生活更轻松。

1

虽然这不是直接回答你的问题,但是你可以为自己保留高度嵌套的Sass,并仍然使用@at-root。在这里查看。

.parent {
  @at-root {
   .child1 { ... }
   .child2 { ... }
  }
} 

// compiles to ... 

.child1 { ... }
.child2 { ... }

2
那怎么有帮助呢?这只会让事情更加混乱。 - Madara's Ghost
我喜欢在Sass文件中使用深度嵌套(可读性,组织性),因此为了避免CSS输出中的复杂选择器,我使用@at-root。正如我所说,这并不完全是对你问题的回答,但如果你倾向于广泛嵌套,这是需要记住的一点 :) - Drops

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接