howieyi

No pains, No gains!

View project on GitHub

雅虎军规

内容部分

  • 尽量减少 HTTP 请求数

    • 合并文件
    • css sprites
    • 图像映射
    • 行内图片(base64)
  • 减少 DNS 查找(dns 预加载)

<meta http-equiv="x-dns-prefetch-control" content="on" />
<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />
<link rel="dns-prefetch" href="http://nsclick.baidu.com" />
<link rel="dns-prefetch" href="http://hm.baidu.com" />
<link rel="dns-prefetch" href="http://eiv.baidu.com" />
  • 避免重定向(重定向使用 301 和 302 状态码)
  • 让 Ajax 可缓存

    • Gzip 组件
    • 减少 DNS 查找
    • 压缩 JavaScript
    • 避免重定向
    • 配置 ETags
  • 延迟加载组件
  • 预加载组件
  • 减少 Dom 元素的数量
  • 跨域分离组件(确保不超过 2-4 个域,存在 DNS 查找的代价)
  • 尽量少用 iframe
    • iframe 优点
      • 引入缓慢的第三方内容,比如标志和广告
      • 安全沙箱
      • 并行下载脚本
    • iframe 缺点
      • 代价高昂,即使是空白的 iframe
      • 阻塞页面加载
      • 非语义
  • 杜绝 404

css 部分

  • 避免使用 css 表达式(强大又危险的方式)
  • 选择 <link>舍弃@import
  • 避免使用滤镜

    • IE 专有的 AlphaImageLoader 滤镜可以用来修复 IE7 之前的版本中半透明 PNG 图片的问题。在图片加载过程中,这个滤镜会阻塞渲染,卡住浏览器,还会增加内存消耗而且是被应用到每个元素的,而不是每个图片,所以会存在一大堆问题。
    • 最好的方法是干脆不要用 AlphaImageLoader,而优雅地降级到用在 IE 中支持性很好的 PNG8 图片来代替。如果非要用 AlphaImageLoader,应该用下划线 hack:_filter 来避免影响 IE7 及更高版本的用户。
  • 把样式表放在顶部(样式表放在 head 里能让页面逐步渲染)

js 部分

  • 去除重复脚本
  • 尽量减少 DOM 访问(用 JavaScript 访问 DOM 元素是很慢的)

    • 缓存已访问过的元素的索引
    • 先“离线”更新节点,再把它们添到 DOM 树上
    • 避免用 JavaScript 修复布局问题
  • 用智能的事件处理器(事件委托)

    • 如果一个 div 里面有 10 个按钮,应该只给 div 容器添加一个事件处理器,而不是给每个按钮都添加一个。
    • 事件能够冒泡,所以可以捕获事件并得知哪个按钮是事件源。
    • DOMContentLoaded 替代 onload
  • 把脚本放在底部

    • 脚本会阻塞并行下载
    • HTTP/1.1 官方文档建议浏览器每个主机名下并行下载的组件数不要超过两个
    • 如果图片来自多个主机名,并行下载的数量就可以超过两个。如果脚本正在下载,浏览器就不开始任何其它下载任务,即使是在不同主机名下的。

JavaScript,css

  • 把 JavaScript 和 css 放到外面

    • 实际上,用外部文件可以让页面更快,因为 JavaScript 和 CSS 文件会被缓存在浏览器。
    • HTML 文档中的行内 JavaScript 和 CSS 在每次请求该 HTML 文档的时候都会重新下载。这样做减少了所需的 HTTP 请求数,但增加了 HTML 文档的大小。
    • 另一方面,如果 JavaScript 和 CSS 在外部文件中,并且已经被浏览器缓存起来了,那么我们就成功地把 HTML 文档变小了,而且还没有增加 HTTP 请求数。
  • 压缩 JavaScript 和 css

    • 压缩具体来说就是从代码中去除不必要的字符以减少大小,从而提升加载速度。
    • 代码最小化就是去掉所有注释和不必要的空白字符(空格,换行和 tab)
    • 代码混淆

      • 混淆是一种可选的源码优化措施,要比压缩更复杂,所以混淆过程也更容易产生 bug。
      • 在对美国前十的网站调查中,压缩可以缩小 21%,而混淆能缩小 25%。虽然混淆的缩小程度更高,但比压缩风险更大。

图片

  • 优化图片(尝试把 GIF 格式转换成 PNG 格式,看看是否节省空间。在所有的 PNG 图片上运行 pngcrush(或者其它 PNG 优化工具))
  • 优化 css Sprite
    • 在 Sprite 图片中横向排列一般都比纵向排列的最终文件小
    • 组合 Sprite 图片中的相似颜色可以保持低色数,最理想的是 256 色以下 PNG8 格式
    • “对移动端友好”,不要在 Sprite 图片中留下太大的空隙。虽然不会在很大程度上影响图片文件的大小,但这样做可以节省用户代理把图片解压成像素映射时消耗的内存。100×100 的图片是 1 万个像素,而 1000×1000 的图片就是 100 万个像素了。
  • 不要用 html 缩放图片
  • 用小的可缓存的 favicon.ico
    • favicon.ico 是放在服务器根目录的图片,它会带来一堆麻烦,因为即便你不管它,浏览器也会自动请求它,所以最好不要给一个 404 Not Found 响应。而且只要在同一个服务器上,每次请求它时都会发送 cookie,此外这个图片还会干扰下载顺序,例如在 IE 中,当你在 onload 中请求额外组件时,将会先下载 favicon。
    • 足够小,不超过 1K
    • 设置合适的有效期 HTTP 头,把有效期设置为几个月后一般比较安全
  • 给 Cookie 减肥(使用 cookie 的原因有很多,比如授权和个性化。HTTP 头中 cookie 信息在 web 服务器和浏览器之间交换。重要的是保证 cookie 尽可能的小,以最小化对用户响应时间的影响。)
    • 清除不必要的 Cookie
    • 保证 Cookie 尽可能小,以最小化对用户响应时间的影响
    • 注意给 Cookie 这只合适的域级别,以免影响其它子域
    • 设置合适的有效期,更早的有效期或者 none 可以更快的删除 Cookie,提高用户响应时间
  • 把组件放在不含 Cookie 的域下
    • 当浏览器发送对静态图像的请求时,cookie 也会一起发送,而服务器根本不需要这些 cookie。所以它们只会造成没有意义的网络通信量,应该确保对静态组件的请求不含 cookie。
    • 可以创建一个子域,把所有的静态组件都部署在那儿。

移动端

  • 保证所有组件都小于 25K
    • 这个限制是因为 iPhone 不能缓存大于 25K 的组件,注意这里指的是未压缩的大小。这就是为什么缩减内容本身也很重要,因为单纯的 gzip 可能不够。
  • 把组件打包到一个复合文档里

服务器

  • Gzip 组件

    • 从 HTTP/1.1 开始,web 客户端就有了支持压缩的 Accept-Encoding HTTP 请求头。(Accept-Encoding: gzip, deflate
    • 如果 web 服务器看到这个请求头,它就会用客户端列出的一种方式来压缩响应。web 服务器通过 Content-Encoding 相应头来通知客户端。(Content-Encoding: gzip)
  • 避免图片 src 属性为空(浏览器会想服务器发送另一个请求)
  • 配置 ETags
    • 实体标签(ETags),是服务器和浏览器用来决定浏览器缓存中组件与源服务器中的组件是否匹配的一种机制(“实体”也就是组件:图片,脚本,样式表等等)。
    • 添加 ETags 可以提供一种实体验证机制,比最后修改日期更加灵活。
    • 一个 ETag 是一个字符串,作为一个组件某一具体版本的唯一标识符。
    • 唯一的格式约束是字符串必须用引号括起来,源服务器用相应头中的 ETag 来指定组件的 ETag
      HTTP/1.1 200 OK
        Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
        ETag: "10c24bc-4ab-457e1c1f"
        Content-Length: 12195
      
    • 然后,如果浏览器必须验证一个组件,它用 If-None-Match 请求头来把 ETag 传回源服务器。如果 ETags 匹配成功,会返回一个 304 状态码,这样就减少了 12195 个字节的响应体。
      GET /i/yahoo.gif HTTP/1.1
        Host: us.yimg.com
        If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
        If-None-Match: "10c24bc-4ab-457e1c1f"
        HTTP/1.1 304 Not Modified
      
  • 对 AJAX 用 get 请求

    • Yahoo!邮箱团队发现使用 XMLHttpRequest 时,浏览器的 POST 请求是通过一个两步的过程来实现的:先发送 HTTP 头,在发送数据。所以最好用 GET 请求,它只需要发送一个 TCP 报文(除非 cookie 特别多)。IE 的 URL 长度最大值是 2K,所以如果要发送的数据超过 2K 就无法使用 GET 了。
    • POST 请求的一个有趣的副作用是实际上没有发送任何数据,就像 GET 请求一样。正如 HTTP 说明文档中描述的,GET 请求是用来检索信息的。所以它的语义只是用 GET 请求来请求数据,而不是用来发送需要存储到服务器的数据。
  • 尽早清空缓冲区

    • 当用户请求一个页面时,服务器需要用大约 200 到 500 毫秒来组装 HTML 页面,在这期间,浏览器闲等着数据到达。PHP 中有一个 flush()函数,允许给浏览器发送一部分已经准备完毕的 HTML 响应,以便浏览器可以在后台准备剩余部分的同时开始获取组件,好处主要体现在很忙的后台或者很“轻”的前端页面上(P.S. 也就是说,响应时耗主要在后台方面时最能体现优势)。
    • 较理想的清空缓冲区的位置是 HEAD 后面,因为 HTML 的 HEAD 部分通常更容易生成,并且允许引入任何 CSS 和 JavaScript 文件,这样就可以让浏览器在后台还在处理的时候就开始并行获取组件。
  • 使用 cdn

    • 记住终端用户 80%到 90%的响应时间都花在下载页面组件上了:图片,样式,脚本,Flash 等等,这是业绩黄金法则。最好先分散静态内容,而不是一开始就重新设计应用程序结构。这不仅能够大大减少响应时间,还更容易表现出 CDN 的功劳。
    • 内容分发网络(CDN)是一组分散在不同地理位置的 web 服务器,用来给用户更高效地发送内容。典型地,选择用来发送内容的服务器是基于网络距离的衡量标准的。例如:选跳数(hop)最少的或者响应时间最快的服务器。
  • 添上 Expires 或者 Cache-Control HTTP 头
    • 对于静态组件:通过设置一个遥远的将来时间作为 Expires 来实现永不失效
    • 多余动态组件:用合适的 Cache-ControlHTTP 头来让浏览器进行条件性的请求