YAOHAIXIAO.COM

HTML(5),CSS(3),JavaScript,DOM,Ajax,JSON,Front-end technologies & Yaohaixiao

热门标签:JavaScript Performance 前端开发 前端性能优化 原创

Rss

Home » Frontend » CSS » CSS学习经验总结 – CSS 性能优化

CSS学习经验总结 – CSS 性能优化

关于CSS性能这个问题,接触前端开发时间长一些的朋友我想大家或多或少会有所了解。我记得我第一次看到关于CSS性能方面的问题,是看YUI TheaterSteve Souders 的演讲 《High Performance Web Sites 14 Rules for Faster Page》 (视频下载地址:souders-performance.m4v)才知道 CSS 书写也要注意性能问题。

CSS 的解析顺序

根据 Steve Souders 的研究,浏览器解析 CSS 的顺序是从右向左的。举个例子:

#navigation .menu li.current a:link {
    background-color: #fff;
    color: #009FBC;
}

类似这样的写法的CSS的解析,浏览器首先会找到页面中所有的链接a标签,然后在遍历找到父节点带class=current的元素,之后又会再遍历所有li标签带class=current,之后再遍历所有class=menu的元素,在遍历id=navigation的节点。是一个从右向左的解析顺序,而并非我们书写CSS的从左向右的顺序。那我给出的这个例子的写法,浏览器在解析这段CSS的解析消耗的时间就会比较长了,性能十分的低。如果你用 google 的 pagespeed 插件测试前端性能的时候,你会发现给出的结论是:Very inefficient rules – good to fix on any page。

使用高性能的CSS选择器

在了解了CSS的解析顺序后,我们会发现,像例子中这样层级非常多的后代选择器的性能是很低的,浏览器解析起来速度会比较慢。其主要原因我在上文解析顺序的时候其实已经提到了,是因为浏览器花了很多时间去遍历,甚至反复遍历比对一些不相干的html元素是否符合所写的CSS规则。所以想提高CSS的解析速度,提升前端性能,我们就应该尽量避免不必要的遍历,用高效的选择器让浏览器快速查找到需要渲染的元素。不过要体现使用高效CSS选择器后性能的提升,必须是在页面结构非常复杂,页面有大量html元素或则大量的CSS代码的时候,你才会感觉到浏览器解析速度的提升

不过,我个人认为,任何能够提升性能的地方,我们前端工程师都应该要注意去优化代码,提高性能。我想大家还记得,在刚开始使用 XHTML + CSS 编写页面的时候,我们很多人包括我自己都写过这样的 reset 代码:

* {
    margin: 0;
    padding: 0;
}

其实 * 通用选择器就是一个低效的选择器,浏览器会对页面中所有元素都进行外边距和内边距清零的初始化。这在一个网站首页,特别是我们具有中国特色的门户网站首页,页面超长,页面含有大量的html元素的时候,使用 * 选择器就是非常低效。推荐使用 YAHOO YUI 框架中提供的 reset.css 中的解决方案:

body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td {
    margin: 0;
    padding: 0;
}

这里说些题外话,我看到有朋友做过什么性能分析,说使用 * 通用选择器重置所有的元素带来的性能损失“非常小”,可以放心的使用它。还是我前面的观点,作为前端工程师,只要你能做到的性能提升(如果成本不大),你都应该去优化,提升前端整体性能。这里提升一些,那里提升一些,整体的前端性能就可能会集腋成裘得到质的飞跃。反之……

好了,回到正题。哪些选择器是高效的呢?或者说哪些选择器是低效的呢?我这里胡诌估计没有人会信,还是看看 google 的分析吧(原文地址:Use efficient CSS selectors):

The following categories of rules are considered to be inefficient:

Rules with descendant selectors (后代选择器)
For example:

Rules with the universal selector as the key (后代选择器和通用选择器一起使用)
body * {...}
.hide-scrollbars * {...}
Rules with a tag selector as the key (后代选择器和标签选择器一起使用)
ul li a {...}
#footer h3 {...}
* html #atticPromo ul li a {...]

Descendant selectors are inefficient because, for each element that matches the key, the browser must also traverse up the DOM tree, evaluating every ancestor element until it finds a match or reaches the root element. The less specific the key [starting from the rightmost selector (called the “key”) ], the greater the number of nodes that need to be evaluated.

后代选择器是低效的,应为浏览器会针对每个符合的html标签向上遍历页面的文档树的父节点有时会一直查找到html标签这个根节点。key使用的越不明确,需要查询遍历的节点就越多(性能也就越低)。

Rules with child or adjacent selectors (子选择器或邻居选择器)
For example:

Rules with the universal selector as the key (和通用选择器一起使用)
body > * {...}
.hide-scrollbars > * {...}
Rules with a tag selector as the key (和标签选择器一起使用)
ul > li > a {...}
#footer > h3 {...}

Child and adjacent selectors are inefficient because, for each matching element, the browser has to evaluate another node. It becomes doubly expensive for each child selector in the rule. Again, the less specific the key, the greater the number of nodes that need to be evaluated. However, while inefficient, they are still preferable to descendant selectors in terms of performance.

子节选择器和邻居选择器是低效的,因为每次都会再查找父节点和临近的上一个节点(2倍的查找了)。key使用的越不明确,需要查询遍历的节点就越多(性能也就越低)。性能虽然低,不过相对后台选择器比较,性能还是要更好一些。

Rules with overly qualified selectors (过度指定的选择器)
For example:
ul#top_blue_nav {...}
form#UserLogin {...}

ID selectors are unique by definition. Including tag or class qualifiers just adds redundant information that needs to be evaluated, needlessly.

id选择是唯一的,再过多的加上类选择器只会增加浏览器查找(对应的标签或类),没有必要。

Rules that apply the :hoverpseudo-selector to non-link elements (给非 a [链接]标签添加hover伪类选择器)
For example:
h3:hover {...}
.foo:hover {...}
#foo:hover {...}
div.faa :hover {...}

The :hover pseudo-selector on non-anchor elements is known to make IE7 and IE8 slow in some cases*.  When a strict doctype is not used, IE7 and IE8 will ignore :hover on any element other than anchors. When a strict doctype is used, :hover on non-anchors may cause performance degradation. 

* See a bug report at http://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=391387

Recommendations 一些书写建议

Avoid a universal key selector.(避免使用通用选择器)
Allow elements to inherit from ancestors, or use a class to apply a style to multiple elements.

允许集成父节点的样式或者使用类选择器应用样式就好了(避免使用通用选择器)。
Make your rules as specific as possible. (CSS选择器越详细确切越好)
Prefer class and ID selectors over tag selectors.
推荐对html标签应用id或则class选择器,其次是标签选择器
Remove redundant qualifiers. (移除多余的修饰)
These qualifiers are redundant:

  • ID selectors qualified by class and/or tag selectors
    给id选择器添加class或者标签选择器
  • Class selectors qualified by tag selectors (when a class is only used for one tag, which is a good design practice anyway).
    给class选择器添加标签选择器的修饰(当然这个class值给一个标签使用的时候就无所谓了)
Avoid using descendant selectors, especially those that specify redundant ancestors.(避免使用后代选择器,尤其是添加过多的祖先层级)
For example, the rule body ul li a {...} specifies a redundant body selector, since all elements are descendants of the body tag.
Use class selectors instead of descendant selectors.
用class选择器代替后代选择器
For example, if you need two different styles for an ordered list item and an ordered list item, instead of using two rules:

ul li {color: blue;}
ol li {color: red;}

You could encode the styles into two class names and use those in your rules; e.g:

.unordered-list-item {color: blue;}
.ordered-list-item {color: red;}

If you must use descendant selectors, prefer child selectors, which at least only require evaluation of one additional node, not all the intermediate nodes up to an ancestor.

Avoid the :hover pseudo-selector for non-link elements for IE clients.

IE浏览器中不要给非链接标签添加:hover选择器
If you use :hover on non-anchor elements, test the page in IE7 and IE8 to be sure your page is usable.   If you find that :hover is causing performance issues, consider conditionally using a JavaScript onmouseover event handler for IE clients.

怎么样?CSS 关于性能的书写规则比你想象的要多吧?另外除了 google 提供的这些 CSS 书写建议,Nicholas C. Zakas 在他和 Nicole Sulliva 合作的项目 CSS Lint 中还提到其他一些关于CSS性能的建议:

  • Don’t use too many web fonts
  • Disallow @import
  • Disallow duplicate background images
  • Disallow selectors that look like regexs
  • Disallow universal selector
  • Disallow unqualified attribute selectors
  • Disallow units for 0 values
  • Disallow overqualified elements
  • Require shorthand properties

如果你还想了解更多关于 CSS Lint 的内容就直接去访问查看吧,我这里就不介绍了。关于 CSS 性能的问题大概就总结到这里吧。

PS:作为一个合格的前端工程师,写 CSS 绝不仅仅是做到浏览器兼容就万事大吉了。性能以及有机会我稍后会聊到的 CSS 代码的可维护性,模块化的 CSS 框架,以及 Nicole Sulliva 提出的 OOCSS 等等问题,是作为前端工程师应该进一步研究的。并且我们应该把掌握的这些知识点贯彻到实际的开发中,不断的优化前端性能,想尽办法给用户带来用户体验更好的前端产品。

声明:本文采用BY-NC-SA协议进行授权。转载请注明转自:CSS学习经验总结 – CSS 性能优化

« »

6 条评论

  • 那个…没有返回顶部 滚来滚去的很费鼠标的…..
    话说博主孩子做链接么
    不嫌弃可以做一个哈

    树上的国
    http://www.anyfog.com
    (贵站已链)

    • 已将加上了这个功能。

    • 你的也做了

  • 你好,友情链接需要写什么 TITLE ?

    • YAOHAIXIAO.COM

  • CSS 性能方面的问题,我想大家在平时可能注意的不多,这里把 google 等相关的资料整理到一起,方便大家参考。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(Spamcheck Enabled)