本文主要参考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 {
…
}
关键选择器为:img
、h1
、p
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