编写高性能可维护的css代码

本文主要参考MDN|编写高效的CSS、译文编写高性能高质量的CSS代码、《高性能网站建设指南》、谷歌|优化性能、《web前端最佳实践》—高性能css等总结而出。感谢前人的智慧!

[TOC]


前言

CSS代码重构的目的:

我们写CSS代码时,不仅仅只是完成页面设计的效果,还应该让CSS代码易于管理,维护。我们对CSS代码重构主要有两个目的:
1、提高代码性能
2、提高代码的可维护性

在这里我主要讲解CSS代码性能提升的一些知识。

性能优化

性能优化是一门做减法的艺术。我们首要要尽力简化页面渲染过程,然后要使渲染过程的每一步都尽量高效。

提高CSS代码性能主要有两个点

1、提高页面的加载性能

提高页面的加载性能,简单说就是减小CSS文件的大小,提高页面的加载速度,尽可以的利用http缓存

2、提高CSS代码性能

不同的CSS代码,浏览器对其解析的速度也是不一样的,如何提高浏览器解析CSS代码的速度也是我们要考虑的

[TOC]

1.CSS选择器

CSS选择器具有高效的继承性,引用Steve Souders的话, CSS选择器效率从高到低的排序如下:

  • ID选择器 比如#header
  • 类选择器 比如.promo
  • 元素选择器 比如 div
  • 兄弟选择器 比如 h2 + p
  • 子选择器 比如 li > ul
  • 后代选择器 比如 ul a 7. 通用选择器 比如 *
  • 属性选择器 比如 type = “text”
  • 伪类/伪元素选择器 比如 a:hover
    以上引用自Steve Souders的Even Faster网站

纵使ID选择器很快、高效,但是它也仅仅如此,,尽量不要再css里面使用id。从Steve Souders的CSS Test我们可以看出ID选择器和类选择器在速度上的差异很小很小。

1.1组合选择器

你可以有一个标准的选择器比如#nav,来选择任何在此元素下的后代元素。此刻,我们读这些是从左到右的方式。我们是先找到#nav,然后从它的里面找其他元素。但是浏览器解析这些不是这样的:浏览器解析选择器是从右到左的方式。

如果想要知道更多浏览器这样解析的原因,请看Stack Overflow上的讨论

1.2关键选择器

渲染样式时重要的是选择器的最后面的部分即为关键选择器(即用来匹配目标元素的那部分,而不是该元素的祖先元素)。在以下例子中将使用该术语讲解。

例如,在下面规则中:

a img, 
div > h1, 
h1 + p {
  …
}

关键选择器为:imgh1p

CSS规范并没有明确浏览器如何去实现样式系统,仅仅是说明了它们必须这样做。有鉴于此,不同的样式系统引擎可能会拥有完全不同的表现和行为,特别是 Gecko 与 WebKit, 这两个引擎都是开源项目,实现了类似的算法,具有极其相近的优缺点。接下来就让我们一睹样式系统如何工作...

[TOC]

2.样式系统工作原理

2.1样式系统如何拆分规则

编写好的CSS代码,有助提升页面的渲染速度。本质上,引擎需要解析的CSS规则越少,性能越好。样式系统将规则拆分成四个主要类别,如下所示,性能依次降低。

  • ID规则
  • class规则
  • 标签规则
  • 通用规则

理解这些分类是十分关键的,因为它们是构建规则匹配块的基础。

ID规则

即包含了那些将 ID 选择器作为关键选择器的规则。

示例

#nav {.....}
a#person-intruc {.....}
#nav[href="#"] {.....}
.....

class规则

如果一个规则将一个 class 明确作为它的关键选择器,那么它就属于该类别。

示例

.nav {.....}
a.person-intruc {.....}
.nav[href="#"] {.....}
.....

标签规则

如果既没有 class 也没有 ID 来明确作为关键选择器,那么接下来的候选者就是 标签 类别。 如果一条规则将一个标签作为它的关键选择器,那么这条规则就属于该类别。

示例

li {.....}
a > img {.....}
a[href="#"]{.....}
p+ul{.....}
.....

通用规则

不属于上面那些类别的规则都属于这个类别。

示例

[hidden="true"] {…} /* A universal rule */  
* {…}       /* A universal rule */
tree > [collapsed="true"] {…} /* A universal rule */

2.2样式系统如何匹配规则

样式系统从关键选择器开始匹配规则,然后左移(查找规则选择器的任何祖先元素)。只要选择器的子树(substree)一直在检查,样式系统就会持续左移,直到和规则匹配,或者是因为不匹配而放弃该条规则。在这其中涉及到规则过滤的基本概念,分类的意义恰恰在此,过滤掉无关的规则(避免浪费时间去匹配)。


现在问题来了,那么什么样式的CSS代码能够更快的匹配,不浪费时间呢?

[TOC]

3.高效的CSS代码编写

3.1避免使用通用规则和单个属性选择器作为关键选择器

*[hidden="true"]等通用规则十分浪费匹配时间。不必要时尽量不使用。

3.2避免过度约束

不要用标签名或 classes 来限定 ID 规则,不要用标签名限定 class 规则

如果规则拥有 ID 选择器作为其关键选择器,则不要为规则增加标签名。因为 ID 是唯一的,增加标签只会没必要地减缓匹配过程。

对于ID的在class这里同样适用。标签可能会随着设计改变,所以仅仅选择严格语义化的名字作为类名并运用是很好的选择。

【注意】定义过多id会使重用性降低,维护更困难,所以不建议多用id。

尽量使用最具体的类别,即能一次搞定就一次搞定。解析速度变慢的罪魁祸首就是标签类别中有过多的规则。通过增加 class 到元素上,我们就可以进一步的将这些规则划分到 Class 类别中,这将减少用于匹配标签的时间。

3.3避免后代选择器

后代选择器是 CSS 中性能耗用最大的选择器。它的性能开销相当大——特别是当选择器在标签或通用类别中。通常我们需要的是 子选择器。不仅性能低下而且代码很脆弱,html代码和css代码严重耦合,html代码结构发生变化时,CSS也得修改。

差
treehead treerow treecell {…}

略好,但还是差(查看下一条指南)
treehead > treerow > treecell {…}

3.4标签分类的规则不要包含子选择器

标签类别的规则中避免使用子选择器。否则的话,在该元素出现的所有地方,匹配时间都将极大延长(特别是当规则很可能会被匹配)。

差
treehead > treerow > treecell {…}
好//采用class规则
.treecell-header {…}

当使用子选择器时要十分谨慎。能免则免。

3.5了解依赖继承,减少代码量

了解哪些属性能够继承,然后允许它们这样做!

不可以继承的属性:
position、z-index、top、right、bottom、left、clip、display、

可以继承的属性:
color、font、text-transform、white-space、tab-size、word-break、word-wrap、overflow-wrap、text-align、text-align-last、text-justify、word-spacing、letter-spacing、text-indent、line-height、text-size-adjus、text-shadow、direction、writing-mode、list-style、list-style-image、list-style-position、list-style-type、table-layout、border-collapse、border-spacing、empty-cells、quotes、cursor、zoom、pointer-events

3.6避免链式(交集)选择符

// bad
.menu.left.icon {..}

// good
.menu-left-icon {..}

3.7尽可能精简规则、尽量不要在选择符定义过多层级,层级越少,同时也降低了css和dom结构的耦合程度,提高样式的可维护性

定义简洁的css规则

使用复合(紧凑)语法

// bad
.someclass {
 padding-top: 20px;
 padding-bottom: 20px;
 padding-left: 10px;
 padding-right: 10px;
 background: #000;
 background-image: url(../imgs/carrot.png);
 background-position: bottom;
 background-repeat: repeat-x;
}

// good
.someclass {
 padding: 20px 10px 20px 10px;
 background: #000 url(../imgs/carrot.png) repeat-x bottom;
}

【特别注意】合理使用简写
你可能知道,以下两行 CSS 代码并不是等价的:

background: rebeccapurple;
background-color: rebeccapurple;

前者是简写,它可以确保让你得到 rebeccapurple 纯色背景;但如果你用的是展开式的单个属性(background-color),那这个元素的背景最终有可能会显示为一个粉色的渐变图案、一张猫的图片、或任何东西,因为同时可能会有一条 background-image 声明在起作用。通常在使用展开式属性的写法时,会遇到这样的问题:展开式写法并不会帮助你清空所有相关的其他属性,从而可能会干扰你想要达到的效果。

了解更多,请参考 CSS 编码技巧

避免不必要的命名空间

// bad
.someclass table tr.otherclass td.somerule {..}

//good
.someclass .otherclass td.somerule {..}

避免不必要的重复

// bad

.someclass {
 color: red;
 background: blue;
 font-size: 15px;
}

.otherclass {
 color: red;
 background: blue;
 font-size: 15px;
}

// good

.someclass, .otherclass {
 color: red;
 background: blue;
 font-size: 15px;
}

在上面规则的基础上,合并相同定义(进一步合并不同类里的重复的规则),删除无效的定义

3.8避免 !important

更多关于!important请仔细阅读该篇文章!important 的重要性:CSS 不变性。建议阅读英语版本,该篇中文翻译某些地方感觉不太恰当。仅仅在不得不使用它的时候辅助规则

3.9避免使用CSS表达式和滤镜

使用CSS 的expression()通常会造成多次运算。实际上,需要用到CSS表达式的地方,通常能够找到其他替代方案,所以避免使用CSS表达式。

那么什么是css expression?

IE5及其以后版本支持在CSS中使用expression,用来把CSS属性和Javascript脚本关联起来,这里的CSS属性可以是元素固有的属性,也可以是自定义属性。就是说CSS属性后面可以是一段Javascript表达式,CSS属性的值等于Javascript表达式计算的结果。在表达式中可以直接引用元素自身的属性和方法,也可以使用其他浏览器对象。

3.10关于@import

import规则一定要先于除了@charset的其他任何CSS规则,尽可能少用该方法引进。在编写scss时如果引进文件需要合并需采用@import "xxx.scss"

示例:

#myDiv {
position: absolute;
width: 100px;
height: 100px;
background:#c00;
left: expression(document.body.offsetWidth - 180   "px");//这儿
top: expression(document.body.offsetHeight - -80   "px");//这儿
text-align:center;
line-height:90px;
color:#fff;
}

[TOC]

4.CSS中的图片处理

4.1不给图片设置不符合自身的尺寸(即缩放)

【解释】:、同一张图片可能会在页面不同地方多次使用,比如缩略图、正常图、大图。问题来了,如果图片原始尺寸和实际需求不同,在使用过程中就会存在性能问题,利用样式缩放会带来cpu的额外计算过程,增加了图片在浏览器的渲染时间,网络传输过程也会占更多带宽,增加下载时间。因此,最佳做法是,为需要的部分单独做一套图片,初始页面加载时就能更快展示。

4.2使用css"雪碧图"

是将零散的图片合并成一张大图,在利用css进行背景定位。好处是减少请求数,提高了图片整体的加载速度。
但它也存在一些缺点:

比如,多张图片合并成大图,需要精确计算,仔细的调整位置,单纯手工制作是一件很复杂的事情。(所幸现在有一些工具可以帮我们做)

另外,维护过程复杂,要尽量让已有的图片保持原来的位置不变,如果是背景图的尺寸发生变化导致原有区域无法放置,那就只好放弃,如果非要在原有位置修改,则剩余的图片样式都需要修改,是很繁琐的过程。新加的图片最好放在最后面

使用不当会导致性能问题,最大的问题就是内存消耗。如果制作过程不做任何的规划,随意摆放,则可能会使图片变得相当大,从而很占内存

建议

  • 雪碧图的尺寸要优化好,空白尽可能少;
  • 及时清理不再使用的图片;
  • 将雪碧图权重做分离,全局/框架级的和局部/模块级的分离开;
  • 缓存设定和更新频率匹配,如果将每天更新的雪碧图的缓存设置到一个月很容易出问题的。

4.3最佳实践

1、在项目后期应用css sprite技术

因为一般在开发过程中,会比较频繁的修改或者更换图片,如果这个时候使用sprite技术,就会增加很多开发成本。

2、合理组织“雪碧”图

如果要把所有的图片放在一张图上面,也会有不妥,维护方面也不会很方便。组织背景图主要按照模块和背景图的风格来划分。比如,作为展示的缩略图放在一起,评论、点赞、上下箭头等图标放在一起等。

3、控制“雪碧”图的尺寸和大小

因为大尺寸的图片会占用大量的内存,所以要控制在合理尺寸,推荐长宽相乘不超过2500,大小在200kb内

4、合理控制背景图单元间的距离及背景图位置

这个原则是为了防止在背景图比元素大小更小的时候,区域出现别的无关背景图

5、借助相关工具处理sprite

你可能感兴趣的:(编写高性能可维护的css代码)