CSS 优先级 (CSS 顺序何时重要)

Avatar of Chris Coyier
Chris Coyier

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

在您日常编写 CSS 的日子里,您可能根本不会想到 CSS 优先级。 它并不总是出现。 但它确实很重要! 每当多个 CSS 选择器匹配具有完全相同 特定性 的元素时,它就会出现。

假设特定性完全相同,顺序 *确实* 重要。

*最后* 声明的样式获胜。

在单个样式表中

假设我们有一些像这样的 HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="module module-foo module-bar">
    Module
  </div>
</body>
</html>

HTML 中属性的顺序*并不* 重要。 样式表中的顺序很重要。 我们来关注一下 background

/* 
  All of these selectors match
  and they all have the same specificity
*/

.module {
  background: #ccc;
  padding: 20px;
  max-width: 400px;
  margin: 20px auto;
}

.module-foo {
  background: orange;
}

/* LAST declared, so these property/values win */
.module-bar {
  background: lightblue; /* I win! */

  /* I still have all the _other_ styles as well */
}

一个有意复杂的示例

顺序*不* 限于单个样式表。 文档中样式表顺序甚至更加重要。

查看这个文档,它包含三个不同的样式……嗯……让我们称之为块。 块可以是 <link rel="stylesheet"><style> 块或 @import 的样式表。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>

  <!-- #1 -->
  <link rel="stylesheet" href="1.css">

  <!-- #2 -->
  <style>
    .module-baz {
      background-color: pink;
    }
  </style>

</head>
<body>
  <div class="module module-foo module-bar module-baz">
    Module
  </div>

  <!-- #3 -->
  <style>
    @import "2.css";
    /*
      Contains
      .module-bar { background: #f06d06; }
    */
  </style>

</body>
</html>

我将这些块标记为 #1、#2 和 #3。 它们都包含具有完全相同特定性的 CSS 选择器。 #3 是最后一个声明的,所以它赢得了优先级之战。

异步加载的 CSS 仍然尊重文档顺序

也许您正在使用像 loadCSS 这样的出色 CSS 加载器来加载 CSS。 如果我们用它来加载第四个 CSS 文件,并且它与上面“复杂”示例中的设置完全相同,会发生什么?

loadCSS 默认情况下将样式表注入到 <head> 的底部,因此它将成为 #3,而 body 底部的 <style> 块将成为 #4,因此获胜。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <link rel="stylesheet" href="1.css">

  <script src="loadCSS.js"></script>
  <script>
    loadCSS("2.css");
  </script>

  <!-- 2.css will be injected right here -->
</head>
<body>
  <div class="module module-foo module-bar module-late">
    Module
  </div>
</body>
</html>

<body> 中将 <link><style> 块作为子级实际上是无效的(尽管它可以工作),因此由 loadCSS 加载的样式表几乎不可能默认情况下不是获胜者。

此外,您可以指定一个 ID 来定位,以便您可以控制 CSS 顺序

<script id="loadcss">
  // load a CSS file just before the script element containing this code
  loadCSS("path/to/mystylesheet.css", document.getElementById("loadcss"));
</script>

关键 CSS 会变得奇怪吗?

您可能根本使用 loadCSS 的原因之一是,您有意尝试延迟加载样式表,因为您正在将 关键 CSS 注入到 <head> 中,以尝试将样式放入第一个数据包中并加快渲染速度。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    /* #1 
       Critical CSS chunk up here */
  </style>
  <script>
    /* #2
       Load the rest of the CSS 
    */
  </script>
</head>
<body>
  <div class="module module-foo module-bar">
    Module
  </div>
</body>
</html>

关键 CSS 的做法是将 CSS 选择器向上移动到更高的块中。 #1 块。 顺序最低且最容易覆盖的块。 因此,理论上,是的,在仅应用关键 CSS 与 CSS 完全加载时,比较页面时,CSS 的应用可能会出现冲突/更改。 但样式表确实完全加载,并且位于关键 CSS 之后,因此最终将按预期进行。

您可能需要微调哪些内容进入关键 CSS,哪些内容不进入——以避免出现奇怪的样式更改闪动。

扩展会变得奇怪吗?

在预处理器中,它们会。

假设您想用变体来设置某样东西的样式

<div class="module module-variation">Module</div>

而预处理器代码(为了演示目的而超级简化)最终会变成这样

%variation {
  background: orange;
}

.module {
  background: #ccc;
  padding: 20px;
  max-width: 400px;
  margin: 20px auto;
}

.module-variation {
  @extend %variation;
}

您可能会想……好吧,.module-variation 是*最后* 声明的选择器,所以它获胜,因此背景应该是橙色。 但它不是,因为扩展将选择器移动到它扩展的内容定义的位置,在本例中是在我们尝试覆盖的内容之前。 编译后的 CSS 是

.module-variation {
  background: orange;
}

.module {
  background: #ccc;
  padding: 20px;
  max-width: 400px;
  margin: 20px auto;
}

因此,background 实际上是 #ccc,而不是我们想要的。 可能最简单的解决方法是使用特定性而不是源顺序。

原生扩展,如果它们 成为现实,可能会不那么令人困惑。

它是一个很傻的事情需要管理

没有人想考虑这个问题。 使用特定性来获胜的样式要容易得多。 但了解它是个好主意,因为不必要的特定性膨胀也很糟糕。

我们继续跳舞。