65.9K
CodeProject 正在变化。 阅读更多。
Home

CSS 架构,第三部分:使用 MetaCoax 方法重构你的 CSS(第一和第二阶段)

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (1投票)

2013 年 5 月 29 日

CPOL

21分钟阅读

viewsIcon

14911

CSS 架构,第三部分:使用 MetaCoax 方法重构你的 CSS(第一和第二阶段)

引言

我在 CSS 架构系列文章的上一篇文章中介绍的顶级可扩展和模块化方法,都包含可以帮助你改变 CSS 思维和结构方式的精彩之处。它们在许多方面也相互重叠,这表明改进 CSS 过程的哪些方面确实至关重要。虽然你可以遵循任何一种方法来成功构建新网站,但事实是,我们大多数人正在试图理解现有失控的 CSS。

因此,虽然我描述的方法本身都很棒,但我们真正需要的是一种方法,可以将它们的所有超级能力结合起来,以对抗混乱代码的邪恶——一种可扩展和模块化技术的“正义 联盟”。此外,正如罗马并非一日建成一样,试图一举纠正成千上万条毫无章法的代码是徒劳的。因此,通过集中的几波来处理代码是一个好主意。

给 CSS 重构一个好名字

去年,我有一个客户项目,我的任务就是做这件事。在研究了 DRY CSS、OOCSS、SMACSS 和 CSSG 之后,我努力将它们提炼成核心实践。灵光一现,我意识到所有这些方法都归结为一句熟悉的格言:“三思而后行。”当然!它们都鼓励查看模式,创建可移植的、可重用的样式和模块,并且不添加不必要的重复选择器或样式。

此外,我的客户有很多代码,并且希望在对 CSS 进行的更改量方面具有灵活性。所以,我制定了一个有凝聚力、分阶段的攻击计划,以减少我客户 CSS 中的行数。到最后,所有四个可扩展 CSS 框架的实践和技术都被整合进来了,我开发的这个过程在减少 CSS 行数方面非常有效。

一旦我创建了流程,我就必须为它命名。以“三思而后行”为基础,我加上了 CSS,结果是:

三思而后行 CSS à mtco CSS à meta coa CSS à MetaCoax!

因此,在这篇文章中,我将与你分享我的 MetaCoax CSS 重构过程,该过程旨在“去臃肿化”成千上万行重复的 CSS——提高 CSS 的可读性、简洁性和可扩展性,同时保持网站的可见设计和功能不变。(查看我最近关于 CSS 去臃肿化演示的幻灯片。)

为了让你准备好重构 CSS,这里有一些建议。首先,要熟悉特异性和层叠——这将产生巨大差异。我希望在本系列的前两篇文章(第一部分第二部分)中,我已经强调了使选择器和规则过于具体会限制其重用性。尤其在使用后代选择器时,特异性会轻易迅速失控,而这正是我们要避免的。其次,记住继承规则:某些属性会被子元素继承;因此,应始终牢记这些属性如何在 DOM 中层叠。

MetaCoax 流程

MetaCoax 流程是一个四阶段的方法。每个阶段都建立在前一个阶段的基础上,它们都包含减少代码量、提高可扩展性和可维护性的实践,并且作为额外的奖励,为面向未来的网站奠定基础。我们将详细介绍每个阶段以及其中包含的实践和技术。在本文中,我将涵盖第一和第二阶段。第三和第四阶段的细节将在本系列的最后一篇文章中出现。

注意:在进行 MetaCoax 重构过程时,一个极好的工具是 Nicole Sullivan 的CSS Lint,它可以识别 CSS 中其他需要清理的地方,并为你提供清理思路。

第一阶段:缩短选择器并利用和分层规则集

第一阶段专注于以最少的工作量来改进网站的 CSS。这些更改涉及修改 CSS,但不触及网站页面的当前 HTML。目标是使样式表更轻量级,并且花费少量时间和精力就能更轻松地维护和更新。该方法涉及优化选择器,同时通过更智能地重用规则集来减少冗余。即使你只应用此阶段的实践来处理你的 CSS,你也会看到可维护性的提高。

这是我们要做的:

  • 缩短选择器链
    • 去除限定符
    • 移除后代选择器
    • 将选择器链限制在三项以内
  • 利用和分层声明
    • 利用层叠,依赖继承
    • 审查、修改和减少 !important 属性
    • DRY(“不要重复自己”)你的规则集

缩短选择器链

为了最好地优化选择器,目标是使用浅层而不是深层选择器链,使链尽可能短。这种做法使代码更易于处理,样式也更具可移植性。其他优点包括减少选择器中断的可能性,减少位置依赖性,降低特异性,并通过防止过度使用 !important 声明来避免特异性战争。

你有多种方法可以缩短选择器链,整合我概述的所有可扩展架构的实践,并进一步应用“减少、重用、回收”的精神。所有这些实践都保证使 CSS 代码更具容错性。而这不正是更新我们样式表的最终目标吗?

移除后代选择器

后代选择器 (a b) 是使用组合选择器来定位元素中最“昂贵”的选择器之一。其他昂贵的 CSS 选择器包括通用选择器 (*) 和子选择器 (a > b)。是什么让它们昂贵?它们非常通用,因此迫使浏览器查看更多页面元素才能进行匹配。选择器链越长,所需的检查越多,浏览器渲染屏幕上的样式所需的时间就越长。匹配后代选择器时,浏览器必须找到页面上键选择器(最右边的那个)的所有实例,然后向上遍历祖先树进行匹配。

虽然对于只有几百行的样式表来说这可能不是问题,但当文档大小接近 10,000 行或更多时,它就成了一个更大的问题。更重要的是,在采用面向未来的移动优先方法时,长选择器链会导致小型、能力较弱的设备被迫加载和处理不必要的庞大 CSS 文档。

过度依赖后代选择器是过去为 Internet Explorer 6 编码时代的遗留问题,因为 Internet Explorer 6 完全不渲染其他 CSS 2.1 组合选择器。由于 Internet Explorer 6 的使用在美国和其他主要市场的用户数量现在几乎为零,因此可以安全地开始使用与 Internet Explorer 7 和 Internet Explorer 8 兼容的选择器,并一劳永逸地放弃对后代选择器的过度使用。表 1 显示了你可以与 Internet Explorer 7 一起使用的选择器。此后的所有 Internet Explorer 版本都支持此处显示的所有选择器。

选择符 Internet Explorer 7
通用 *
子选择器: e > f
属性选择器: e[attribute]
:first-child
:hover
:active
同胞/相邻选择器: e + f n
:before n
:after n

表 1. Internet Explorer 7 安全的 CSS 2.1 选择器

注意:请查看https://caniuse.cn/http://www.findmebyip.com/litmus/ 上的图表,以确定其他 CSS 选择器的浏览器支持情况。

使用子选择器代替后代选择器。子选择器选择直接是父元素的后代元素——也就是说,第一代的直接子元素——而不是后代选择器包含的孙子或曾孙。图 1 说明了此选择过程。

 

图 1. 后代选择器 vs. 子选择器

虽然子选择器仍然是一个“昂贵”的选择器,但它更具体。这意味着浏览器不需要在继承链中搜索那么远来匹配键选择器。因此,这样的选择器能更好地定位你需要的元素。

如果你必须使用后代选择器,请移除其中所有不必要的元素。例如:

.widget li a

将变为

.widget a

无论 li 是否存在,样式都会被应用。

去除限定选择器

用元素限定 #IDs 和 .classes 会不必要地减慢浏览器搜索页面上其他元素以匹配选择器的速度。限定 ID 永远没有必要。ID 在 CSS 中具有最高的特异性权重之一,因为它在页面上是唯一的,并且它总是会自行进行直接匹配。限定选择器还会导致选择器的特异性极高,需要使用更具体的选择器和 !important 来覆盖这些超特异性规则集。

例如,像这样的选择器: 

div#widget-nav div#widget-nav-slider

可以简化为

#widget-nav
#widget-nav-slider

并进一步精简为

#widget-nav-slider

每一种都能达到相同的效果。

从选择器中移除元素类限定符会降低选择器的特异性,这能更好地使你能够正确使用层叠来覆盖样式(如果需要)。例如:

li.chapter

最好改为

.chapter

更好的是,因为它更具体到 <li> 的情况,你可以考虑更改 <li> 标签上的类,并将 CSS 范围限定为

.li-chapter

.list-chapter

限制在三项以内

在优化选择器时,请执行“三项以内”的规则:组合选择器到达键选择器最多应有三个步骤。例如,看这个选择器:

div#blog-footer div#col2.column
div.bestright p.besttitle {margin-bottom: 4px;}

为了使到达键选择器有三个步骤或更少,请进行类似这样的更改:

#col2.column .besttitle {border:
1px solid #eee;}

利用和分层声明

接下来要处理的是样式声明本身。在将臃肿的 CSS 重构为更易于管理的内容时,很容易主要关注选择器,认为样式会自动处理好。然而,关注你创建的样式声明(参见图 2)以及它们的位置,对于实现简洁的 CSS 也很重要。

 

图 2. CSS 规则集解剖

利用继承

通常,我们认为自己非常了解某件事,但实际上我们并不了解,而 CSS 中的继承可能就是其中之一。你可能还记得继承是 CSS 的基本概念,但你可能不确切地记得哪些属性自然继承,哪些不继承。表 2 显示了最常用的会被后代元素继承的属性,除非后代元素被另行设置样式。(还有其他更晦涩的属性也会被继承。)

color

font-family

font-family

font-size    

font-style

font-variant

font-weight  

font

letter-spacing

line-height

list-style-image    

list-style-position

list-style-type     

list-style

text-align

text-indent  

text-transform

visibility

white-space

word-spacing

表 2. 后代元素通常会继承的常见元素

在寻找可合并或消除的重复样式时,记住这些属性很重要。在更新样式表时,应将可继承的属性放在 CSS 中,以便最佳地利用它们,而不是重复。通过正确放置这些属性,以后可以完全消除重复的样式声明。

审查、修改和减少 !important 属性

如果你的 CSS 中有大量 !important 声明,那么是时候减少它们了。你真的应该只在特定情况下使用 !important 声明。Chris Coyier(CSS Tricks 的创始人)建议与实用类或用户样式表一起使用它们。如果以其他方式使用,你可能会被贴上自私和懒惰的标签,谁想这样呢?!

如何减少 !important 的使用?首先,通过遵循我之前的建议,保持选择器的特异性较低。其次,请记住,理想情况下,新样式不应撤销之前的规则集,而应在之上添加。

我的意思是:如果你发现自己编写新样式来撤销旧样式(然后使用 !important 来试图覆盖该样式以应对特异性战争),那么你需要重新思考旧样式,将其提炼到其必要性,然后创建可以增强原始样式的新样式,而不是试图撤销已有的内容。这就是我所说的“分层”样式规则集。这也可以称为“扩展”(或“子类化”)样式,这是第二阶段创建模块的一部分。

如果你对同一样式有大量 !important 属性,我敢打赌那些属性可以变成一个可移植样式,可以应用于多个元素,我将在描述第二阶段时也会谈到这一点。

DRY 你的规则集

为了减少 CSS 中大量重复的样式,一点 DRY 编码会有所帮助。虽然采用完整的 DRY CSS 方法可能有点严厉,但要注意何时重复相同的规则集,然后让你的团队进行优化是一个很好的实践

第二阶段:重构、调整和模块化

第二阶段的技术侧重于进行中等到高水平的工作来改进网站的 CSS。更改包含修改页面的 CSS 和 HTML,对 HTML 的更改最可能涉及重命名或重新分配类名。目标是通过按类别而不是按页面分组样式,移除旧式 HTML,清除选择器中的多余内容,并创建模块以提高代码效率,从而为样式表提供结构和组织。

此级别会进一步消除冗余,通过提高选择器准确性和效率使样式表更轻量级,并有助于维护。这个级别的改进比第一阶段需要更多的时间和精力,但它包含了使你的 CSS 更好的大部分工作,并预计会大幅减少 CSS 代码行数。

这是我们要做的:

  • 重构以优化
    • 在样式表中对 CSS 规则进行分类
    • 重构依赖于 DOM 中较高限定符的样式
    • 使用类名作为键选择器
  • 开始建立模块
    • 用双连字符 (--) 扩展子模块样式
  • 创建可移植的辅助样式
    • 外科式布局助手
    • 排版样式
  • 调整 HTML
    • 消除内联样式
    • 减少 <span> 的使用以获得更好的语义

重构以优化

不要忘记,重构 CSS 是我们的主要目标。这些实践开始了一个过程,即从思考和创建基于页面组件和页面层次结构并且特定的样式,转向以可移植、可重用和模块化的方式思考样式。

在样式表中对 CSS 规则进行分类

本系列的第一篇文章中,我建议创建目录,以便更容易地找到 CSS 中样式的各个部分。在这个 CSS 重构阶段,我建议将该过程提升几个档次,将这些部分转换为它们所描述的样式类型,遵循SMACSS 类别。这些类别是:

  • Base 默认样式,通常是贯穿整个文档的单个元素选择器。
  • Layout 页面部分的样式。
  • Module 网站各个模块的可重用样式:呼出框、侧边栏部分、产品、媒体、幻灯片、列表等。
  • State 描述模块或布局在特定状态下外观的样式。
  • Theme 描述模块或布局外观的样式。

因此,现在你的目录和文档部分将如下所示:

/* Table of Contents
- Base
- Layout
- Module
- State
- Theme
*/
…
(later in the document…)
/* =Layout */ (etc.)

这种样式表的重组有助于为第二阶段的其他实践奠定基础,也是第三阶段的一部分。

重构依赖于 DOM 中较高限定符的样式

此建议是本文最重要的建议之一:完全消除特定于页面的样式——即基于向 body 元素添加类以表示不同页面的样式。这样的样式会迫使浏览器一直检查到 <body> 标签。以下是一个示例:

body.donations.events
div#footer-columns div#col1 div.staff span.slug {
display: block;
margin: 3px 0 0 0;
}

这种做法是长选择器链、极高的选择器特异性以及使用 !important 来覆盖层叠更高处的样式的根源,如下例所示:

body.video div#lowercontent
div.children.videoitem.hover a.title { background: #bc5b29; 
color: #fff !important; 
text-decoration: none; 
}

换句话说,这很糟糕。好吧

要解决这个问题,你需要遵循所有之前的建议,例如三项以内、去除限定符和降低特异性。你最终可能会得到类似这样的结果:

.donations-slug {
display: block;
margin: 3px 0 0 0;
}

使用类名作为键选择器

由于 ID 的特异性很高,你应该尽量避免使用它们,因为它们不能像类一样被重用。但是,在创建类时,你希望保持类名具有语义且可移植。目标是使选择器尽可能直接。通过这样做,你可以避免特异性问题,然后甚至可以组合样式来分层它们,如前所述。

根据 SMACSS,你应该遵循一个实践:在创建选择器时,键选择器应该是 .class 而不是标签名或 #id。始终牢记最右边的键选择器是最重要的。如果一个选择器可以尽可能具体地定位到相关元素,那么你就成功了。这样,样式就被更直接地定位,因为浏览器只匹配精确的元素。

审查所有使用子选择器的地方,并在可能的情况下用具体的类替换它们。这也避免了子组合器中的键选择器成为元素,这也是不鼓励的。

例如,与其这样做:

#toc > LI > A

最好创建一个类,如下所示,然后将其添加到相应的元素中。

.toc-anchor

开始建立模块

没有什么比模块更能体现可扩展 CSS 方法中的“三思而后行”格言了,模块是它们的核心。模块是代码的组成部分,可以从设计模式中抽象出来——例如,列表项经常带有浮动到左侧、右侧或下方的图像;这些可以抽象成一个模块,具有每个模块共享的基本样式集。然后,模块可以通过文本、背景、字体颜色、边框、浮动等的更改来扩展(或着色),但结构保持不变。

模块最好的地方在于它们是可移植的,这意味着它们不依赖于位置。将设计模式抽象成代码模块意味着你可以将其放在任何页面的任何位置,并且它会以相同的方式显示,而无需在样式方面重新发明轮子。现在,这就是 CSS 的用途,对吧?!除了我之前的建议,模块化你的 CSS 是大幅减少代码量的最佳方法之一。

OOCSS 提供了一种很好的思考如何构建给模块着色以及什么可以制成模块的方法。SMACSS 提供了有关如何命名模块和扩展模块的清晰指南

用 -- 扩展子样式

虽然 SMACSS 提供了关于扩展模块的良好指导,但我更喜欢 CSS for Grownups 中用 -- 扩展子样式的技术。这对我很重要,因为它是一个视觉指示,表明新样式基于之前的样式,但又超越了它。

这里有一个例子:

#go, #pmgo{
  width: 29px;
  height: 29px;
  margin: 4px 0 0 4px;
  padding: 0;
  border: 0;
  font-size: 0;
  display: block;
  line-height: 0;
  text-indent: -9999px !important;
  background: transparent url("/images/go.jpg") 0 0
no-repeat;
  cursor: pointer; /* hand-shaped cursor */
  x-cursor: hand; /* for IE 5.x */
}
 
#pmgo{
  margin: 2px 0 0 3px;
  background: transparent url("/images/go.jpg")
no-repeat center top;
}

这段代码可以修改并变成更像这样:

.button-search{
  width: 29px;
  height: 29px;
  margin: 4px 0 0 4px;
  padding: 0;
  border: 0;
  font-size: 0;
  display: block;
  line-height: 0;
  text-indent: -9999px !important;
  background: transparent url("/images/go.jpg") 0 0
no-repeat;
  cursor: pointer; /* hand-shaped cursor */
  x-cursor: hand; /* for IE 5.x */
}
.button-search--pm{
  margin: 2px 0 0 3px;
  background: transparent url("/images/go.jpg")
no-repeat center top;
}

创建可移植的辅助样式

除了模块化过程,可移植样式也是你工具库中的另一个实用工具。以下是 CSS for Grownups 中的一些示例。

外科式布局助手

根据 CSS for Grownups 的标准,拥有一些额外的布局帮助并不可耻。虽然网格可以解决很多问题,但这些样式可以在需要时(尤其是在垂直间距方面)对元素进行一点微调,同时减少代码行数。

.margin-top {margin-top:
5px;}
.margin-bottom
{margin-bottom: .5em;}

虽然在大多数情况下,我们在创建类时会努力保持语义化的名称,但在这种情况下,描述性名称也可以。 

排版样式

如果你发现你的 CSS 中大部分都用于更改字体、大小和/或行高,那么排版样式是完美的。OOCSS 和 CSS for Grownups 都建议使用不与元素绑定的专用排版样式,例如:

.h-slug {font-size: .8em;}
.h-title {font-size: 1.5em;}
.h-author {font-size: 1em;}

一个很好的练习是搜索属性 font、font-size、font-face 和 h1 到 h6,并惊叹于这些属性出现的数量之多。一旦你笑了,就找出哪些样式适用于什么,哪些大小很重要,然后开始创建一些可移植的排版样式。

调整 HTML

虽然我们到目前为止所涵盖的阶段 1 和阶段 2 中的实践在 CSS 清理方面提供了奇迹,但我们不能忘记页面标记。很可能,正如 Andy Hume 在 CSS for Grownups 中建议的那样,你需要对 HTML 进行更改,而不仅仅是添加新的类名。

减少 <span> 的使用以获得更好的语义

你是否过度使用了 <span> 标签,而更具语义的标签会更合适?请记住,根据 W3C 的说法(http://www.w3.org/TR/html401/struct/global.html#h-7.5.4),<span> 标签实际上是用于行内元素的,而不是块级元素,因此将 <span> 用于标题和其他打算成为块级元素的元素在技术上是不正确的。

例如,这里的 span 实际上应该是段落或标题标签,以表明此内容在文档层次结构中的位置。其他任一元素都可以为类钩子提供基础。

<li
class="item">
<a href="https://codeproject.org.cn/learning/hillman"
title="">
<img src="https://codeproject.org.cn/images/brenda-hillman.jpg"
alt="Air in the Epic" />
</a>
<span
class="slug">Brenda Hillman Essays</span>
<span
class="title"><a href="https://codeproject.org.cn/learning/hillman"
title="Air in the Epic" class="title">Air in the Epic</a></span>
<span
class="author">Brenda Hillman</span>
</li>

从语义学的角度来看,以下版本会是一个改进:

<li
class="item">
<a href="https://codeproject.org.cn/learning/hillman"
title="">
<img src="https://codeproject.org.cn/images/brenda-hillman.jpg"
alt="Air in the Epic" />
</a>
<p
class="slug">Brenda Hillman Essays </p>
<h3
class="title"><a href="https://codeproject.org.cn/learning/hillman"
title="Air in the Epic" class="title">Air in the Epic</a></h3>
<p
class="author">Brenda Hillman</p>
</li>

消除内联样式

最后,你需要摆脱内联样式。在当今时代,内联样式应该很少使用,甚至从不使用。它们与 HTML 的关联过于紧密,就像过去充斥着 <font> 标签的时代一样。如果你使用内联样式来覆盖特异性,那么进行本文提出的更改应该已经可以帮助你避免特异性战争,从而有效地消除对内联样式的需求。

例如,这种内联样式:

<span
class="text-indent: 1em">Skittles are tasty</span>

可以轻松地变成它自己的类,可以应用于整个文档,如下所示:

.indent {text-indent: 1em:}

你的任务,如果你选择接受它,就是找到所有内联样式的实例,并看看在哪里可以使这些样式成为可移植的辅助工具。你正在使用的属性可以很容易地变成一个可重用的可移植样式,用于其他文本实例。

试试吧,这对你很有好处!

在瑞典马尔默举行的 Øredev 网页开发会议上,我有幸听了才华横溢的 Katrina Owen 的演讲“治疗性重构”。她建议,在面临最后期限时,她会通过重构糟糕的代码来帮助她获得对世界的控制感和正确感。

你可以用同样的方式来重构你的 CSS 以求改进。通过治愈你 CSS 中的病症,你可以获得平静和力量感,同时开始让你的网站样式世界变得更美好,一次一行代码。

但请继续关注,因为我们还没有完全结束。我们还有两个阶段将在 MetaCoax 流程中介绍,它们将彻底根除你 CSS 中的邪恶,并使你能够为所有后续的前端开发者留下良善的遗产。

本文是 Internet Explorer 团队 HTML5 技术系列的一部分。在 BrowserStack 免费跨浏览器测试三个月尝试本文中的概念 @http://modern.IE

进一步阅读链接

本文由 Denise R. Jacobs 撰写。Denise 是备受尊敬的 Web 设计专家,拥有超过 14 年的行业经验。她现在做着她最喜欢的事情:成为一名演讲者 + 作者 + Web 设计顾问 + 创意传播者。在 Twitter 上以其“很棒的资源”而广受赞誉(@denisejacobs),Denise 是《CSS Detective Guide》一书的作者,该书是关于 CSS 代码故障排除的首选书籍,也是《Interact with Web Standards》和《Smashing Book #3: Redesign the Web》的合著者。她最新的个人项目是鼓励更多来自代表性不足群体的人通过成为知名的 Web 专家来Rawk the Web。你可以通过 denise@denisejacobs.com 联系到她,更多信息请访问 DeniseJacobs.com

© . All rights reserved.