《高性能网站建设进阶指南》总结

在知乎上看到有人推荐这本《高性能网站建设进阶指南》,趁着自己还在学校赶紧去图书馆借来看了看,书不厚也就200多页很快就看完了,但图书馆的书最让人不爽的就是不能勾画,于是也就有了这篇总结。

在《高性能网站建设进阶指南》之前,作者还出版过《高性能网站建设指南》一书,这本书里为Web性能提升设置了14条规则:

  1. 尽量减少 HTTP 请求
  2. 使用 CDN
  3. 添加 Expires 头
  4. 采用 Gzip 压缩组件
  5. 将样式表放在顶部
  6. 将脚本放在底部
  7. 避免 CSS 表达式
  8. 使用外部的 javascript 和 CSS
  9. 减少 DNS 查询
  10. 精简 javascript
  11. 避免重定向
  12. 删除重复的脚本
  13. 配置 ETag
  14. 使 Ajax 可缓存

下面是《高性能网站建设进阶指南》的章节总结:

第一章 理解 Ajax 性能

做性能优化时,不要浪费时间去尝试为那些不消耗大量时间的代码提速。

通常浏览器在运行javascript上花费的时间很少,绝大部分时间消耗在 DOM 上。

Ajax 提供了取代页面替换的有效方案,善加利用 Ajax 能使应用程序运行的更快。

第二章 创建快速响应的 WEB 应用

如何定义快速呢?

javascript 代码执行时间超过 0.1 秒,页面会给人不够平滑快捷的感觉;执行时间超过 1秒,则会感到应用程序缓慢;超过 10 秒,用户会非常沮丧。

javascript 是单线程的!

在页面上任何开销很大的(例如,长时间运行)javascript 操作都应该委托给 Web Worker。

在 XMLHttpRequest 的异步模式中,XHR 实质上就是一个拥有专用 API 的 Web Worker。

内存对响应时间的影响

  • 在 GC 执行回收时,会冻结整个运行环境,直到遍历完整个创建对象的“堆”。随着应用程序内存占用的增加,遍历整个堆去查找用户不再使用的对象所需要的时间最终会引起用户的注意。
  • 网页内存需求增长到足够大,迫使操作系统开始内存分页,系统把内存页从物理内存转移到虚拟内存(例如从 RAM 到硬盘)。

如果确定内存有问题,可以通过以下两种方式清理内存:

  • 使用 delete 关键字从内存中移除不再需要的 javascript 对象
  • 从 DOM 树中移除不再是必须的节点

#第三章 拆分初始化负载
可以把 javascript 代码拆分到不同的组,第一组包含初始化页面所必须的函数,剩下的则在这些代码需要执行时按需加载它们,或者等到初始化的那些 javascript 代码加载完毕时再加载。

第四章 无阻塞加载脚本

浏览器在下载和解析 javascript 脚本时,不仅会阻塞页面中其他内容的下载,还会阻塞脚本后面所有元素的渲染。

浏览器在下载和执行脚本时出现阻塞的原因在于,脚本可能会改变页面或 javascript 的命名空间。

很显然 javascript 脚本没有必要按顺序下载,这方面 IE8 走在了前面,IE8 是第一个支持脚本并行下载的浏览器。

下面列出异步加载脚本的技术:

  • XHR eval:该技术用过 XHR 从服务器获得脚本,然后通过 eval 执行脚本内容。
  • XHR 注入:类似于 XHR eval,但是与 eval 不同的是,该机制是通过创建一个 script 的 DOM 元素,然后把 XHR 的响应注入到 script 中执行 javascript。
  • Script DOM Element:该技术使用 javascript 动态创建 script DOM 元素并设置其 src 属性。下载过程中用这种方式创建脚本不会阻塞其他组件。和前面的技术相比,该技术允许跨域获取脚本。
  • Script defer 属性:HTML 4 为 script 标签定义了一个扩展属性:defer。带有 defer 属性的 JavaScript 文件下载时,它不会阻塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载。带有 defer 属性的 script 标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到 script 标签时开始下载,但不会执行,直到 DOM 加载完成,即onload事件触发前才会被执行。但是,defer 属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持,所以它不是一个理想的跨浏览器解决方案。
  • Script async 属性:HTML 5 为 script 标签定义了一个新的扩展属性:async。它的作用和 defer 一样,能够异步地加载和执行脚本,不因为加载脚本而阻塞页面的加载。但是有一点需要注意,在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。

第六章 布置行内脚本

在样式表后面的行内脚本会阻塞所有后续资源的下载,解决该问题的方法是调整行内脚本的位置,使其不出现在样式表和任何其他资源之间。

第七章 编写高效的 javascript

  • 尽量避免使用会增长作用域链的结构,比如使用 with 语句和 try-catch 语句中的 catch 从句。如果非局部变量的使用超过一次,那么为了降低性能损耗,应该把它存储到一个局部变量中。例如,如果函数中多次访问处于作用域链底层的document,可以把 document 赋值给一个局部变量,从而减少降低在作用域链中搜索变量的时间。
  • 存储和读取数据的方式对脚本的性能影响很大。局部变量和字面量总是最快的,存取数组元素和对象属性会引起性能损耗。如果数组元素或对象的属性使用超过一次,那么为了提高存取速度,应该把它存储到一个局部变量中。
  • 在 javascript 中,循环经常成为性能瓶颈,为了使循环高效,可以使用倒序的方式来处理元素,即在控制条件中,将迭代变量和 0 作比较。
  • 流控制也是影响脚本执行速度的一个重要因素。if 语句适用于少量离散值或一段区间值的判断;switch 语句最好用于 3~10 个离散值的判断。
  • 谨慎的使用 DOM 中的 HTML nodelist 对象,每次存取这类对象的属性,都会重新查询 DOM 中的匹配节点。为了避免这种高昂的开销,只有必要时才存取 nodelist 对象,并将经常存取的值存储在局部变量中。例如,当用 for 遍历通过 getElementsByTagName 得到的 nodelist 对象时,应该把 nodelist 的 length 赋值给一个局部变量。
  • 如果 javascript 代码运行时间过长,可以使用定时器把任务拆分执行。

#第九章 超越Gzip压缩
所有的浏览器都支持 Gzip压缩,并可以通过添加 Accept-Encoding 的 http 头来向 web 服务器声明支持压缩:

Accept-Encoding: gzip, deflate

当请求中包含这个头时, web 服务器就会开启 Gzip 压缩功能。

第十章 图像优化

各种图片格式介绍

GIF

  • 只支持二进制透明(要么完全透明,要么完全不透明)
  • 支持动画
  • 是无损格式
  • gif 有 256 色的限制,不适合显示照片

JPEG

  • 不支持透明
  • 不支持动画
  • 是有损格式
  • ,压缩比很高,适合显示照片

PNG

  • 支持 alpha 透明
  • 不支持动画
  • 是无损格式
  • 不是动画时,应该尽可能 使用 png8(调色板png) 代替 gif。

图像优化建议

  • 首先要选择合适的格式:用 jpeg 保存照片,用 gif 保存动画,其他所有图像用 png 保存,并且尽量使用 png8
  • 不要在 html 中对图像进行缩放
  • 不要忘了 favicons
  • 使用并优化 css sprite

第十一章 划分主域

IE6、7等老浏览器把从同一个服务器并行下载的资源数限制为2个,IE8、firefox和chrome增加到了6个。

浏览器执行 “每个服务器端最大连接数” 的限制是根据 URL 上的主机名,而不是解析出来的 IP 地址。

可以把网页中的资源放在不同的服务器上,加速资源的下载。

第十三章 少用 iframe

使用 iframe 会带来的问题:

  1. iframe 是开销最高的 DOM 元素,创建 iframe 的开销比创建其他类型的 DOM 元素要高 1~2 个数量级。
  2. 使用 iframe 会阻塞页面的 onload 事件,延长了浏览器的忙指示。
  3. 虽然 iframe 是一个完全独立的文档,浏览器也会共享对每个浏览器的受限连接数(IE8、chrome为6个)

第十四章 简化 CSS 选择符

CSS 的选择符类型(从开销最小到最大排序):

  1. ID 选择符#
  2. 类选择符.
  3. 类型选择符
  4. 相邻兄弟选择符 +
  5. 子选择符
  6. 后代选择符
  7. 通配符选择符
  8. 属性选择符
  9. 伪类

事实上,CSS 选择符是从右向左进行匹配的!最右边的选择符被称为关键选择符,关键选择符应该尽量使用开销小的选择符。