2009年6月29日星期一

[翻译]减少 reflow

浏览器为了重新渲染部分或整个页面,重新计算页面元素位置和几何结构(geometries)的进程叫做 reflow。由于 reflow 是一种浏览器中的用户拦截(user-blocking)操作,所以了解如何减少 reflow 次数,及不同的文档属性(DOM 层级(DOM depth),CSS 效率,不用类型的 style 变化)对 reflow 次数的影响对开发者来说非常必要。有时 reflow 页面中的一个元素会 reflow 它的父元素(译注:这里是复数)以及所有子元素。

有多种用户操作和 DHTML 变化可能会触发 reflow。调整浏览器窗口的大小,用 javascript 计算样式(computed styles),在 DOM 中创建删除元素,改变元素的 class 都会触发 reflow。值得注意的是,有些操作会多次触发 reflow,超出你的想象。下图源自 Steve Souders 的演讲 "Even Faster Web Sites":

从上表可以很明显的看出,在所有浏览器中并非所有 javascript 控制的样式都触发 reflow,即使触发了触发的次数也不尽相同。同时可以看出现代浏览器在控制 reflow 次数方面做的越来越好。

 在 Google,我们通过多种方式对我们的页面及 Web 应用测速,同时 reflow 是我们增加 UI 时考虑的一个关键因素。我们致力于传达轻快的(lively),交互性强的(interactive)和令人愉悦的(delightful)的用户体验。

原则

下面是一些减小 reflow 的原则:

  1. 减少不必要的 DOM 层级(DOM depth)。改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行 reflow 上面。
  2. 尽量减少 CSS 规则,去除未用到的 CSS。
  3. 如果做复杂的表现变化,如动画,让它脱离文档流。用绝对定位或 fixed 定位来完成。
  4. 避免不必要的复杂的 CSS 选择器,尤其是后代选择器(descendant selectors),因为为了匹配选择器将耗费更多的 CPU。

在下面的视频中(译注:引用自 youtube,请翻墙),Lindsey 介绍了一些减少 reflow 的方法。

延伸阅读

[翻译]减小 HTML 文件

改善网站速度的一个很明显的方法是减小 HTML 文件的大小。有一些方法,比如:硬压缩(rigid compression),acupuncture-like ID 及改变 class 名(译注:这一句不是很确定,原文:There are several ways to do this, from rigid compression to acupuncture-like ID and class name changes)。下面是一些我们总结的使 HTML 标记更精简的方法。

译注:上面是引用 youtube 的视频,请翻墙观看。

HTML 4

HTML (非XHTML),MIME type 为 text/html ,允许省略一些标签。通过 HTML 4 DTD,你可以省略以下标签(那些所谓可避免的元素,这里用删除线加以标记(感谢一楼的翻译))

</area>
</base>
<body>
</body>
</br>
</col>
</colgroup>
</dd>
</dt>
<head>
</head>
</hr>
<html>
</html>
</img>
</input>
</li>
</link>
</meta>
</option>
</p>
</param>
<tbody>
</tbody>
</td>
</tfoot>
</th>
</thead>
</tr>

比如,你的代码是

<li>List item</li>

可以写为

<li>List item

又比如段落要以

</p>

结尾,你可以只写

<p>My paragraph

甚至可以去掉 html,head,body(把这作为你的编码规范之前请确保这会令你舒服)。

省略标签后 HTML 依然有效,同时减小了文件大小。对一般的页面来说,可以节省 5-20%。

HTML 5

正在发展中的 HTML 5 提供了一些减小文件大小的方法。

比如,页面文档类型声明

<!DOCTYPE html>

对比

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

很显然 HTML 5 的 DTD 更短。

当为页面指定编码时,HTML 5 很易用而且更短:

<meta charset="utf-8">

代替

<meta http-equiv="content-type" content="text/html; charset=utf-8">

通常情况下,浏览器会正确处理 HTML。

另外,在今天的 HTML 5 中,你可以去除声明 MIME 类型的 type 属性,比如

type="text/css"
或
type="text/javascript"

你可以用

<script>
替代
<script type="text/javascript">
用
<style>
替代
<style type="text/css">

在所有类型的页面中(甚至是 XHTHML)你可以省略

type="text/css"

HTML 5 使这一切变得更简单。

同时使用上面所有的方法会使文件节省 10%-20%(甚至更多),这取决于你的编码风格和页面中的文本内容数量。代码将更干净,访问者会更快的获取网站内容。在隐私中心项目中我们采用很多这类技术,节省了原始文件大小的 20%。

2009年6月28日星期日

[翻译]谨慎使用 Iframe

使用 iframe 可以轻易的调用其他网站的页面,但应谨慎使用。它比创建其他 DOM 元素(包括 style 和 script)多耗费数十甚至数百倍的性能。增加100个不同元素的时间对比显示 iframe 是多么耗费性能:

使用 iframe 的页面通常没有这么多 iframe,所以创建 DOM 的时间不用多虑。更值得关心的是 onload 事件和连接池。

iframe 阻塞 onload

window 的 onload 事件尽快执行非常重要。这会让浏览器的载入进度指示器完成,用户依据此判断页面是否已经加载完。而 onload 事件延迟,会让用户感觉页面变慢。

window 的 onload 事件直到它所包含的所有 iframe,以及所有 iframe 中的资源完全加载完成后才会触发。在 Safari 和 Chrome 中,用 javascritpt 动态的给 iframe 的 src 赋值可以避免这种阻塞行为。

一个连接池

对每个 web 服务器来说,浏览器只打开极少的几个连接数。老的浏览器,包括 IE 6/7 和 Firefox 2,每个主机只有2个连接。在新的浏览器中,连接数增加鸟。Safari 3+ 和 Opera 9+ 增至4个,Chrome 1+ 、IE 8 及 Firefox 3 增至6个。对比表格详见《并行连接数总结》。

人们可能期望每个 iframe 有单独的连接池,但并非如此。在大多数浏览器中,连接被主页面和它的 iframe 所共享,这意味着有可能 iframe 中的资源占用了可用连接而阻塞了主页面的资源加载。如果 iframe 中的内容同等重要,或比主页面更重要,这很好。然而在通常情况下 iframe 中的内容对页面来说不太重要,iframe 占用连接数是不可取的。一个解决方案是在优先级更高的资源下载完成后再动态的给 iframe 的 src 赋值。

美国的10大网站中有5个使用了 iframe。它们多数用来加载广告。这不是很合适,但可以理解,这是一个简便的在内容中插入广告的途径。在很多情况下,使用 iframe 是合理的。但要意识到这对你的页面的性能影响。非必要时,请谨慎使用。

2009年6月23日星期二

谨慎使用 CSS Sprites

CSS sprites 是网站速度的优化很重要的一环,但也有其对性能的不利之处。

Vladimir Vukićević 的博文《To Sprite Or Not To Sprite》提到:

CSS sprites 的最大问题是内存占用。非精确构造的 sprite 图片会占用意想不到的内存空间。以 WHIT TV 网站为例,这里是一张 sprite 图片,1299x15,000 的png,已经经过很好的压缩,实际下载大小只有26K左右,但是浏览器不会转换压缩的图像数据。当图片下载并解压,将耗费75MB内存(1299 * 15000 * 4)。如果图片没有阿尔法透明,可能会减小到1299 * 15000 * 3,可往往还是牺牲了渲染速度。即便如此,我们占用了55MB内存。这张图片绝大部分是空白的,什么都没有,没有什么有用的内容。仅仅因为这张图片,当浏览器只加载WHIT 主页时会增加75+MB内存。

Mozilla Web Development Blog 在《 Use Sprites Wisely》中总结道:

简而言之,即使是很小的 sprite 图片也有可能吃掉大量的系统内存 -- 每个页面50M甚至100M或者更多。速度虽然至关重要,但要意识到 sprite 及其他 hacks 同样会影响用户体验。

现在再回过头看之前的《Yahoo与Gmail的CSS Sprites对比》,综合性能方面 Gmail 无疑占了上风。当然,这些只是前端层面的优化,从根源上来看,设计师用最少的图片来实现最优的效果才是王道。

正如雅虎女工程师 Nicole Sullivan 在 Velocity 2009 大会演讲 PPT 《The Fast And The Fabulous》所提到的:

consistent design = clean code = fast site(一致的设计 = 更干净的代码 = 更快的网站)

2009年6月19日星期五

[翻译]简化CSS选择器

本文是《Even Faster Web Sites: Performance Best Practices for Web Developers (Paperback)》的最后一章。上篇帖子《Performance Impact of CSS Selectors》(中文版)最后提出了一段假设:

对大多数网站而言,优化CSS选择器活得的性能提升很小,不值得去计较。有些配合Javascript交互的CSS规则会明显的拖慢页面。这是应该关注的焦点。所以我开始关注现实中影响页面性能的CSS样式相关的小问题。

我收到了很多反馈。David Hyatt的文章《Writing Efficient CSS for use in the Mozilla UI》披露:

样式系统渲染一条规则是从最右边开始之后依次向左移动。在你的小子树(subtree)持续检测的时候,样式系统将继续向左侧移动直到它不匹配CSS规则或匹配错误。

由此得出,我们优化工作的重点应该是:匹配大量页面元素的最右侧的CSS选择器。我上篇博文测试的CSS选择器看起来很费性能,但是按这条新观点审视,我们发觉这其实不值得担心,比如:

DIV DIV DIV P A.class0007 {}

这个选择器有5层,看起来很复杂,但是我们来看最右侧的选择器 A.class0007 ,我们发现,在整个页面中需要浏览器逆向匹配的只有一个元素。

优化CSS选择器的关键点在于最右侧的选择器,也叫做key selector (巧合?)。有一个更昂贵的选择器

A.class0007 * {}

尽管这个选择器看起来更简单,但对浏览器匹配而言更昂贵。因为浏览器要从右至左,开始后要检查匹配 * 的所有元素。这意味着浏览器会尝试匹配页面中的所有元素。下图为普通选择器与先前的后代选择器加载时间的对比:

它清晰的反映出一个匹配很多元素的key selector会严重的拖慢页面。其他可能会大量增加浏览器工作的key selector包括:

A.class0007 DIV {}
#id0007 > A {}
.class0007 [href] {}
DIV:first-child {}

不是所有的CSS选择器伤害性能,尽管看起来如此。CSS选择器的关键点在于泛匹配的key selector。这对于含有大量DOM元素、CSS规则,更高 reflow 的Web 2.0应用更加重要。