今天伟大的交互设计师高高问到表单select控件能否限制最大宽度,于是我随手写了个用max-width + IE6 expression的实现方案(写完DEMO才发现IE7不支持select的max-width)。最近花花群里也有人问expression性能优化的问题,这里就一并说下。
Yslow、PageSpeed以及各类性能优化的原则中通常都会包含一条“Avoid CSS Expressions”,关于这条优化原则的成因与解决方案, Steve Souders早在N年前就写有专题详细阐述过。
当我第一次看到DEMO中的expression counter飞速增长时,内心十分震撼,我发现自己津津乐道地使用expression来模拟IE6下的position:fixed效果是一件多么愚蠢的事情。有时我们乐于人云亦云,却害怕思考。“嗯,不能使用css expression,不能使用alpha filter,因为有很严重的性能问题。”然而问题在哪里?影响程度究竟有多大?有没有办法减小甚至消除这种影响?很少有人能给出答案。这点在做前端面试的时候体会尤其深刻,很多面试者都有不错的前端经验,知晓一些常见的bug及hack方案,可一旦加深技术交流,都是一问三不知。
Steve Souders是一个很好的榜样(更多前端榜样),他教会我前端页面性能优化不能按部就班的停留在教科书层面,必须要深入理论、大量实践。其实做任何工作又何尝不是如此,这同样是专家与熟练工的区别所在。
回到正题,放上最后实现的DEMO效果(用IE67查看expression效果),顺便简单说下one-time expression的优化方法。
先看第一个下拉选择框的CSS代码:
.select1{max-width:200px;*width: expression(counter('1'),(this.offsetWidth > 200) ? '200px' : this.offsetWidth + 'px');}
CSS expression的原理可以理解为,expression()的整个调用语句作为该CSS属性的属性值,在页面渲染过程中该调用的返回值再作为CSS属性的属性值进行渲染。这意味着当页面加载完毕后,一旦再有repaint/reflow,CSS表达式都会重新执行计算一次。
在以上这段代码中,逗号运算符(counter(’1′)后的“,”号)的返回值是逗号右边的语句,因此expression的返回值是三目运算符的返回值,通过这种方法我们增加了counter监测函数。一旦表达式执行,计数器会加1。通过观察页面计数器的数值变化,我们可以发现一旦鼠标hover页面任意元素,计数器都会飞速的增长!在页面scroll时,效果会更恐怖。这点就是expression性能影响的成因。
再贴出优化后的CSS代码:
.select2{max-width:200px;*width: expression(function(el){el.style.width = el.offsetWidth > 200 ? '200px' : el.offsetWidth + 'px';counter('2');}(this));}
通过创建一个匿名函数的闭包,我们可以在expression中自由的写更多语句,因为这个匿名函数没有return语句,所以预计执行完成后会返回undefined的值给*width属性。然而在该条expression首次执行时,该select的width值就已经被 “el.style.width = …”语句所复写,导致在CSS样式表对象中的CSS规则已经由expression语句替换成一个具体的值。因此在优化过后的DEMO中,expression的执行次数永远为1。
Yslow“Avoid CSS Expressions”的最后一段中提到:
One way to reduce the number of times your CSS expression is evaluated is to use one-time expressions, where the first time the expression is evaluated it sets the style property to an explicit value, which replaces the CSS expression. If the style property must be set dynamically throughout the life of the page, using event handlers instead of CSS expressions is an alternative approach. If you must use CSS expressions, remember that they may be evaluated thousands of times and could affect the performance of your page.