最近,我们看到很多从初学者到经验丰富的开发人员都在为 CSS 而苦恼。有些人不喜欢 CSS 的工作方式,并想知道用其他语言代替 CSS 是否更好——CSS 处理器就是从这种想法中诞生的。有些人使用 CSS 框架,希望他们能够编写更少的代码(我们在 之前的一篇文章 中已经看到过,为什么这种情况通常并非如此)。有些人开始完全放弃 CSS,而使用 JavaScript 来应用样式。
但是你并不总是需要在你的工作流程中包含 CSS 处理器。你不需要将臃肿的框架作为任何项目的默认起点。而且使用 JavaScript 来完成 CSS 应该做的事情仅仅是一个糟糕的想法。
在这篇文章中,我们将看到一些编写更易于维护的 CSS 代码的技巧和建议,这样你的样式表就会更短,并且规则更少。CSS 可以成为一个方便的工具,而不是负担。
“最小的可行选择器”
CSS 是一种声明式语言,你可以在其中指定规则,这些规则将为 DOM 中的元素设置样式。在这门语言中,有些规则在应用顺序中具有优先级,例如内联样式会覆盖某些先前的规则。
例如,如果我们有以下 HTML 和 CSS 代码
<button class="button-warning">
.button-warning {
background: red;
}
button, input[type=submit] {
background: gray;
}
尽管 .button-warning
在 button, input[type=submit]
规则之前定义,但它将覆盖后者的 background
属性。为什么?决定哪个规则会覆盖另一个规则的样式的标准是什么?
特异性.
有些选择器被认为比其他选择器更具特异性:例如,#id
选择器会覆盖 .class
选择器。
如果我们使用了一个比实际需要的更具特异性的选择器会怎样?如果我们以后想要覆盖这些样式,我们需要一个更具特异性的选择器。如果我们以后需要覆盖这个更具特异性的选择器,我们需要…是的,这是一个不断增长的雪球,最终会变得非常难以维护。
因此,无论何时编写选择器,都要问自己:这是这里可以完成工作的最不具特异性的选择器吗?
所有特异性规则都在 W3C CSS 选择器规范 中正式定义,这是了解 CSS 选择器所有细节的方法。为了更容易理解,可以阅读 关于 CSS 特异性的这篇文章。
不要用新规则来解决错误
让我们想象一下这种情况:你的 CSS 中有一个错误,并且你找到了哪个 DOM 元素具有错误的样式。并且你意识到它以某种方式继承了一个它不应该具有的属性。
不要只是用更多的 CSS 来解决它。如果你这样做,你的代码库会变得更大,并且定位未来的错误会更难。
相反,停下来,退一步,使用浏览器中的开发者工具来检查元素并查看整个级联。准确地确定哪个规则正在应用你不需要的样式。并修改该现有规则,使其不再具有意外的后果。
在 Firefox 中,你可以通过右键单击页面上的元素并选择 检查元素
来调试级联。
看看所有这些荣耀的级联。在这里,你可以看到应用于元素的所有规则,以及它们的应用顺序。最顶部的条目是那些具有更高特异性的条目,并且可以覆盖先前的样式。你可以看到一些规则中有一些属性被删除了:这意味着一个更具特异性的规则正在覆盖该属性。
并且你不仅可以看到规则,还可以实时地打开和关闭它们,或者更改它们并观察结果。它非常适合修复错误!
所需的修复可能是一个规则更改,也可能是在级联中的不同位置进行规则更改。修复可能需要一个新规则。至少你会知道这是一个正确的选择,并且是你的代码库需要的。
这也是寻找重构机会的好时机。尽管 CSS 不是一种编程语言,但它也是源代码,你应该像对待 JavaScript 或 Python 一样对待它:它应该简洁、可读,并在需要时进行重构。
不要使用 !important
之前的建议已经暗示了这一点,但由于它至关重要,我想强调一下:不要在你的代码中使用 !important
。
!important
是 CSS 中的一个功能,它允许你打破级联。CSS 代表“层叠样式表”,这是一个提示。
!important
通常在你匆忙修复错误并且没有时间或意愿修复你的级联时使用。当你在包含 具有非常特异性规则的 CSS 框架 时,它也被广泛使用,并且覆盖它们太难了。
当你将 !important
添加到一个属性时,浏览器会忽略其他具有更高特异性的规则。当你要!important
一个规则来覆盖另一个被标记为 !important
的规则时,你就知道你真的遇到了麻烦。
!important
只有一个合法用途——在使用开发者工具调试某些东西时。有时你需要找出哪个属性的值可以修复你的错误。在开发者工具中使用 !important
并实时修改 CSS 规则,可以让你在忽略级联的情况下找到这些值。
一旦你知道了哪些 CSS 部分将起作用,你就可以回到你的代码中,看看你想在级联的哪个位置包含这些 CSS 部分。
除了 px 和 % 之外还有其他方法
使用 px
(像素)和 %
(百分比)单位非常直观,因此我们将在这里重点关注不太为人所知或不太直观的单位。
Em
和 rem
最著名的相对单位是 em。1em 等效于该元素的字体大小。
让我们想象一下以下 HTML 部分
<article>
<h1>Title</h1>
<p>One Ring to bring them all and in the darkness bind the.</p>
</article>
以及一个只有以下规则的样式表
article {
font-size: 1.25em;
}
大多数浏览器默认情况下将 16 像素的基线字体大小应用于根元素(顺便说一下,这可以通过用户覆盖——这是一个很好的可访问性功能)。因此,该文章元素的段落文本可能会以 20 像素(16 * 1.25
)的 font-size
进行渲染。
h1
怎么办?为了更好地理解会发生什么,让我们将以下其他 CSS 规则添加到样式表中
h1 {
font-size: 1.25em;
}
尽管它也是 1.25em
,与 article
相同,但我们必须考虑到 em
单位是累积的。也就是说,例如,h1
作为 body
的直接子级,将具有 20 像素(16 * 1.25)的 font-size
。但是,我们的 h1
位于一个与根元素(我们的 article
)具有不同 font-size
的元素内。在这种情况下,1.25
指的是级联给出的 font-size
,因此 h1
将以 25 像素(16 * 1.25 * 1.25)的 font-size
进行渲染。
顺便说一下,与其在脑子里进行所有这些乘法链,不如直接使用 检查器
中的 计算
选项卡,它将以像素显示实际的最终值
em
单位非常灵活,可以非常轻松地更改(甚至动态地更改)页面的所有大小(不仅是 font-size
,还有其他属性,如 line-height
或 width
)。
如果你喜欢 em
的“相对于基线大小”部分,但不喜欢累积部分,可以使用 rem
单位。rem
单位就像 忽略累积并只使用根元素大小的 em
。
因此,如果我们使用我们之前的 CSS,并将 h1
中的 em
单位更改为 rem
article { font-size: 1.25em; }
h1 { font-size: 1.25rem; }
所有 h1
元素都将具有 20 像素(假设 16px
基线大小)的计算 font-size
,无论它们是否位于文章内。
vw 和 vh
vw
和 vh
是视窗单位。 1vw
是视窗宽度的 1%,而 1vh
是视窗高度的 1%。
如果你需要一个需要占据整个屏幕的 UI 元素(比如模态对话框的典型半透明深色背景),它们非常有用,这些元素并不总是与实际的 body
大小相关。
其他单位
还有其他单位可能不像以前那么常见或通用,但你不可避免地会遇到它们。你可以在 MDN 上 了解更多关于它们的信息。
使用 flexbox
我们在关于 CSS 框架的 之前的一篇文章 中谈到了这一点,但 flexbox 模块简化了制作布局和/或对齐事物的任务。如果你不熟悉 flexbox,请查看 这个入门指南。
而且是的,你今天就可以使用 flexbox。除非你出于商业原因确实需要支持旧版本的浏览器。目前的 浏览器对 flexbox 的支持率超过 94%。因此你可以停止编写所有那些难以调试和维护的浮动 div
。
此外,请关注即将发布的 Grid 模块,它将使布局实现变得轻而易举。
使用 CSS 处理器时…
Sass 或 Less 等 CSS 编译器在前端开发领域非常流行。它们是强大的工具,可以让我们更高效地使用 CSS。
不要滥用选择器嵌套
这些处理器或“编译器”的一个常见功能是选择器嵌套。例如,以下 Less 代码
a {
text-decoration: none;
color: blue;
&.important {
font-weight: bold;
}
}
将被转换为以下 CSS 规则
a {
text-decoration: none;
color: blue;
}
a.important {
font-weight: bold;
}
此功能允许我们编写更少的代码,并对影响 DOM 树中通常一起出现的元素的规则进行分组。这对于调试很有用。
但是,滥用此功能也很常见,最终会导致在 CSS 选择器中复制整个 DOM。因此,如果我们有以下 HTML
<article class="post">
<header>
<!-- … -->
<p>Tags: <a href="..." class="tag">irrelevant</a></p>
</header>
<!-- … -->
</article>
我们可能会在 CSS 样式表中找到以下内容
article.post {
// ... other styling here
header {
// ...
p {
// ...
a.tag {
background: #ff0;
}
}
}
}
主要缺点是这些 CSS 规则具有极其具体的选择器。我们已经看到,这是我们应该避免的事情。过度嵌套还有其他缺点,我在另一篇文章中对此进行了讨论。
简而言之:不要让嵌套生成你不会自己键入的 CSS 规则。
包含与扩展
CSS 处理器的另一个有用功能是混合器,它们是可重复使用的 CSS 代码块。例如,假设我们要为按钮设置样式,并且大多数按钮都具有一些基本 CSS 属性。我们可以在 Less 中创建一个像这样的混合器
.button-base() {
padding: 1em;
border: 0;
}
然后创建一个像这样的规则
.button-primary {
.button-base();
background: blue;
}
这将生成以下 CSS
.button-primary {
padding: 1em;
border: 0;
background: blue;
}
如您所见,重构公共代码非常方便!
除了“包含”混合器之外,还可以选择“扩展”或“继承”它(确切的术语因工具而异)。这样做会将同一规则中的多个选择器组合在一起。
让我们看一个使用之前.button-base
混合器的示例
.button-primary {
&:extend(.button-base)
background: blue;
}
.button-danger {
&:extend(.button-base)
background: red;
}
这将被转换为
.button-primary, .button-danger {
padding: 1em;
border: 0;
}
.button-primary { background: blue; }
.button-danger { background: red; }
网上有一些文章告诉我们只使用“包含”,而另一些文章告诉我们只使用“扩展”。事实是它们会生成不同的 CSS,它们都不是天生错误的,并且根据您的实际情况,使用其中一个或另一个会更好。
如何选择它们?同样,适用“我会手动编写这些代码吗?”的经验法则。
我希望这可以帮助您反思您的 CSS 代码并编写更好的规则。请记住我们之前所说的:CSS 是代码,因此与您代码库中的其他部分一样,值得同样程度的关注和呵护。如果你给予它一些关爱,你将获得回报。
关于 Belén Albeza
Belén 是一位在 Mozilla 开发者关系部门工作的工程师和游戏开发者。她关心网络标准、高质量代码、可访问性和游戏开发。
25条评论