性能优化
- 减少 Http 请求:
1. 尽量减少 HTTP 请求数 80% 的终端用户响应时间都花在了前端上,其中大部分时间都在下载页面上的各种组件:图片,样式表,脚本,Flash 等等。减少组件数必然能够减少页面提交的 HTTP 请求数。这是让页面更快的关键。
2. 减少页面组件数的一种方式是简化页面设计。但有没有一种方法可以在构建复杂的页面同时加快响应时间呢?嗯,确实有鱼和熊掌兼得的办法。
3. 合并文件是通过把所有脚本放在一个文件中的方式来减少请求数的,当然,也可以合并所有的 CSS。如果各个页面的脚本和样式不一样的话,合并文件就是一项比较麻烦的工作了,但把这个作为站点发布过程的一部分确实可以提高响应时间。
4.CSS Sprites 是减少图片请求数量的首选方式。把背景图片都整合到一张图片中,然后用 CSS 的 background-image 和 background-position 属性来定位要显示的部分。
5. 图像映射可以把多张图片合并成单张图片,总大小是一样的,但减少了请求数并加速了页面加载。图片映射只有在图像在页面中连续的时候才有用,比如导航条。给 image map 设置坐标的过程既无聊又容易出错,用 image map 来做导航也不容易,所以不推荐用这种方式。
6. 行内图片(Base64 编码)用 data: URL 模式来把图片嵌入页面。这样会增加 HTML 文件的大小,把行内图片放在(缓存的)样式表中是个好办法,而且成功避免了页面变“重”。但目前主流浏览器并不能很好地支持行内图片。
7. 减少页面的 HTTP 请求数是个起点,这是提升站点首次访问速度的重要指导原则。
- 减少 DNS 查找:
1. 域名系统建立了主机名和 IP 地址间的映射,就像电话簿上人名和号码的映射一样。当你在浏览器输入 www.yahoo.com 的时候,浏览器就会联系 DNS 解析器返回服务器的 IP 地址。DNS 是有成本的,它需要 20 到 120 毫秒去查找给定主机名的 IP 地址。在 DNS 查找完成之前,浏览器无法从主机名下载任何东西。
2.DNS 查找被缓存起来更高效,由用户的 ISP(网络服务提供商)或者本地网络存在一个特殊的缓存服务器上,但还可以缓存在个人用户的计算机上。DNS 信息被保存在操作系统的 DNS cache(微软 Windows 上的”DNS 客户端服务”) 里。大多数浏览器有独立于操作系统的自己的 cache。只要浏览器在自己的 cache 里还保留着这条记录,它就不会向操作系统查询 DNS。
3.IE 默认缓存 DNS 查找 30 分钟,写在 DnsCacheTimeout 注册表设置中。Firefox 缓存 1 分钟,可以用 network.dnsCacheExpiration 配置项设置。(Fasterfox 把缓存时间改成了 1 小时 P.S. Fasterfox 是 FF 的一个提速插件)
4. 如果客户端的 DNS cache 是空的(包括浏览器的和操作系统的),DNS 查找数等于页面上不同的主机名数,包括页面 URL,图片,脚本文件,样式表,Flash 对象等等组件中的主机名,减少不同的主机名就可以减少 DNS 查找。
5. 减少不同主机名的数量同时也减少了页面能够并行下载的组件数量,避免 DNS 查找削减了响应时间,而减少并行下载数量却增加了响应时间。我的原则是把组件分散在 2 到 4 个主机名下,这是同时减少 DNS 查找和允许高并发下载的折中方案。
- 避免重定向 重定向用 301 和 302 状态码,下面是一个有 301 状态码的 HTTP 头:
1.HTTP/1.1 301 Moved Permanently Location: http://example.com/newuri Content-Type: text/html 浏览器会自动跳转到 Location 域指明的 URL。重定向需要的所有信息都在 HTTP 头部,而响应体一般是空的。其实额外的 HTTP 头,比如 Expires 和 Cache-Control 也表示重定向。除此之外还有别的跳转方式:refresh 元标签和 JavaScript,但如果你必须得做重定向,最好用标准的 3xxHTTP 状态码,主要是为了让返回按钮能正常使用。
2. 牢记重定向会拖慢用户体验,在用户和 HTML 文档之间插入重定向会延迟页面上的所有东西,页面无法渲染,组件也无法开始下载,直到 HTML 文档被送达浏览器。
3. 有一种常见的极其浪费资源的重定向,而且 web 开发人员一般都意识不到这一点,就是 URL 尾部缺少一个斜线的时候。例如,跳转到 http://astrology.yahoo.com/as…://astrology.yahoo.com/astrology/ 的 301 响应(注意添在尾部的斜线)。在 Apache 中可以用 Alias,mod_rewrite 或者 DirectorySlash 指令来取消不必要的重定向。
4. 重定向最常见的用途是把旧站点连接到新的站点,还可以连接同一站点的不同部分,针对用户的不同情况(浏览器类型,用户帐号类型等等)做一些处理。用重定向来连接两个网站是最简单的,只需要少量的额外代码。虽然在这些时候使用重定向减少了开发人员的开发复杂度,但降低了用户体验。一种替代方案是用 Alias 和 mod_rewrite,前提是两个代码路径都在相同的服务器上。如果是因为域名变化而使用了重定向,就可以创建一条 CNAME(创建一个指向另一个域名的 DNS 记录作为别名)结合 Alias 或者 mod_rewrite 指令。
- 让 Ajax 可缓存:
1.Ajax 的一个好处是可以给用户提供即时反馈,因为它能够从后台服务器异步请求信息。然而,用了 Ajax 就无法保证用户在等待异步 JavaScript 和 XML 响应返回期间不会非常无聊。在很多应用程序中,用户能够一直等待取决于如何使用 Ajax。例如,在基于 web 的电子邮件客户端中,用户为了寻找符合他们搜索标准的邮件消息,将会保持对 Ajax 请求返回结果的关注。重要的是,要记得“异步”并不意味着“即时”。
2. 要提高性能,优化这些 Ajax 响应至关重要。最重要的提高 Ajax 性能的方法就是让响应变得可缓存,就像在添上 Expires 或者 Cache-Control HTTP 头中讨论的一样。下面适用于 Ajax 的其它规则:
3.Gzip 组件 减少 DNS 查找 压缩 JavaScript 避免重定向 配置 ETags 我们一起看看例子,一个 Web 2.0 的电子邮件客户端用了 Ajax 来下载用户的通讯录,以便实现自动完成功能。如果用户从上一次使用之后再没有修改过她的通讯录,而且 Ajax 响应是可缓存的,有尚未过期的 Expires 或者 Cache-Control HTTP 头,那么之前的通讯录就可以从缓存中读出。必须通知浏览器,应该继续使用之前缓存的通讯录响应,还是去请求一个新的。可以通过给通讯录的 Ajax URL 里添加一个表明用户通讯录最后修改时间的时间戳来实现,例如 &t=1190241612。如果通讯录从上一次下载之后再没有被修改过,时间戳不变,通讯录就将从浏览器缓存中直接读出,从而避免一次额外的 HTTP 往返消耗。如果用户已经修改了通讯录,时间戳也可以确保新的 URL 不会匹配缓存的响应,浏览器将请求新的通讯录条目。
4. 即使 Ajax 响应是动态创建的,而且可能只适用于单用户,它们也可以被缓存,而这样会让你的 Web 2.0 应用更快。
- 延迟加载组件:
可以凑近看看页面并问自己:什么才是一开始渲染页面所必须的?其余内容都可以等会儿。
1.JavaScript 是分隔 onload 事件之前和之后的一个理想选择。例如,如果有 JavaScript 代码和支持拖放以及动画的库,这些都可以先等会儿,因为拖放元素是在页面最初渲染之后的。其它可以延迟加载的部分包括隐藏内容(在某个交互动作之后才出现的内容)和折叠的图片。
2. 工具可帮你减轻工作量:YUI Image Loader 可以延迟加载折叠的图片,还有 YUI Get utility 是一种引入 JS 和 CSS 的简单方法。Yahoo! 主页就是一个例子,可以打开 Firebug 的网络面板仔细看看。
3. 最好让性能目标符合其它 web 开发最佳实践,比如“渐进增强”。如果客户端支持 JavaScript,可以提高用户体验,但必须确保页面在不支持 JavaScript 时也能正常工作。所以,在确定页面运行正常之后,可以用一些延迟加载脚本增强它,以支持一些拖放和动画之类的华丽效果。
- 预加载组件:
预加载可能看起来和延迟加载是相反的,但它其实有不同的目标。通过预加载组件可以充分利用浏览器空闲的时间来请求将来会用到的组件(图片,样式和脚本)。用户访问下一页的时候,大部分组件都已经在缓存里了,所以在用户看来页面会加载得更快。
实际应用中有以下几种预加载的类型:
1. 无条件预加载——尽快开始加载,获取一些额外的组件。google.com 就是一个 sprite 图片预加载的好例子,这个 sprite 图片并不是 google.com 主页需要的,而是搜索结果页面上的内容。条件性预加载——根据用户操作猜测用户将要跳转到哪里并据此预加载。在 search.yahoo.com 的输入框里键入内容后,可以看到那些额外组件是怎样请求加载的。提前预加载——在推出新设计之前预加载。经常在重新设计之后会听到:“这个新网站不错,但比以前更慢了”,一部分原因是用户访问先前的页面都是有旧缓存的,但新的却是一种空缓存状态下的体验。可以通过在将要推出新设计之前预加载一些组件来减轻这种负面影响,老站可以利用浏览器空闲的时间来请求那些新站需要的图片和脚本。
- 减少 DOM 元素的数量:
一个复杂的页面意味着要下载更多的字节,而且用 JavaScript 访问 DOM 也会更慢。举个例子,想要添加一个事件处理器的时候,循环遍历页面上的 500 个 DOM 元素和 5000 个 DOM 元素是有区别的。
1. 大量的 DOM 元素是一种征兆——页面上有些内容无关的标记需要清理。正在用嵌套表格来布局吗?还是为了修复布局问题而添了一堆的 <div>s?或许应该用更好的语义化标记。
2.YUI CSS utilities 对布局有很大帮助:grids.css 针对整体布局,fonts.css 和 reset.css 可以用来去除浏览器的默认格式。这是个开始清理和思考标记的好机会,例如只在语义上有意义的时候使用 <div>,而不是因为它能够渲染一个新行。
3.DOM 元素的数量很容易测试,只需要在 Firebug 的控制台里输入:
document.getElementsByTagName(‘*’).length
4. 那么多少 DOM 元素才算是太多呢?可以参考其它类似的标记良好的页面,例如 Yahoo! 主页是一个相当繁忙的页面,但只有不到 700 个元素(HTML 标签)。
- 跨域分离组件:
1. 分离组件可以最大化并行下载,但要确保只用不超过 2 - 4 个域,因为存在 DNS 查找的代价。例如,可以把 HTML 和动态内容部署在 www.example.org,而把静态组件分离到 static1.example.org 和 static2.example.org。
后续还会整理,目前对性能的优化先写这么多 ……..