# 雅虎军规 35 条

  • 来自 Google 数据表明,一个 10 条数据 0.4s 加载完页面,变为 30 条数据 0.9s 加载完,用户的流量会下降 90%
  • GoogleMap 首页文件大小从 100kb 减小到 70-80kb 后,流量在第一周涨了 10%,接下来的三周涨了 25%
  • 亚马逊的数据表明:加载时间增加 100 毫秒,销量就下降 1%

# 前端性能优化

# 页面内容

  • 减少 HTTP 请求数
  • 减少 DNS 查询
  • 避免重定向
  • 缓存 Ajax 请求
  • 延迟加载
  • 预加载
  • 减少 DOM 元素数量
  • 划分内容到不同域名
  • 尽量减少 iframe 使用
  • 避免 404 错误

# 减少 HTTP 请求数

80%的终端用户响应时间都花在了前端上,其中大部分时间都在下载页面上的各种组件:图片,样式表,脚本....所以减少 http 请求是让页面变快的关键

  • 合并 js/css 文件。服务器端(CDN)自动合并,基于 Node.js 的文件合并工具,通过把所有脚本放在一个文件中的方式来减少请求数。
  • 使用 css sprite 将背景图合并为一个文件,通过background-imagebackground-position控制显示
  • 图片映射,把多张图片合并为一张【image map 不容易,所以不推荐】
  • 行内图片(base64 编码):使用data:url将图片嵌入到 HTML 或 css 中。【会有兼容问题】

css 和 js 合并为一个文件

  • 利用 css, js 对注释不同的编译:css 会忽略,但 js 会将其作为注释符号如同"//"
// **.jscss
<!-- /*
  js代码
<!-- */
<!--
  css代码

虽然**.jscss被链接了两次,但因为缓存,只会下载一次

# 减少 DNS 查询

域名系统建立了主机名和 IP 地址间的映射,就像电话薄上的人名和号码的映射一样。当你在浏览器输入www.yahoo.com的时候,浏览器就会联系 DNS 解析器返回服务器的 IP 地址。DNS 是有成本的,它需要 20ms-120ms 去查找给定的主机名的 IP 地址。在 DNS 查找完成前,浏览器无法从主机名下载任何东西。

DNS 查找被缓存起来更高效,由用户的 ISP(网络服务提供商)或本地网络存在一个特殊缓存服务器上,但还可以缓存在个人用户的计算机上。DNS 信息被保存在操作系统的 DNS cache(微软 Windows 上的“DNS 客户端服务”)里。大多数浏览器有独立于操作系统的自己的 cache。只要浏览器在自己的 cache 里还保留这条记录,它就不会向系统查询 DNS。

IE 默认缓存 DNS 查询 30 分钟,写在 DNSCacheTimeout 注册表设置中。Firefox 缓存 1 分钟,可以用network.dnsCacheExpiration配置项设置。(Fasterfox 把缓存时间改为 1 小时 P.S。Fasterfox 是 FF 的一个提速插件)

如果客户端 DNS cache 是空(包括浏览器和操作系统),DNS 查找数等于页面上不同主机名数,包括 URL,图片,脚本文件,样式表,flash 对象等组件中的主机名,减少不同的主机名就可以减少 DNS 查询。

另外一面,减少不同主机名数量,同时也减少了页面能够并行下载的组件数量,避免 DNS 查找消减了响应时间,但减少并行下载数量却增加了响应时间。原则上,可以吧组件分散大到 2-4 个主机名下,这是减少 DNS 查找和允许高并发下载的折中方案。

# 避免重定向

重定向用 301 和 302 状态码

HTTP/1.1 301 Moved Permanently
  Location http://example.com/newUti
  Content-Type: text/html

浏览器会自动跳转到 Location 域指明的 URL。重定向需要的所有信息都在 HTTP 头部,而响应体一般是空的。其实额外的 HTTP 头,比如 Expires 和 Cache-Control 也表示重定向,除此之外还有别的跳转方式:refresh 元标签和 JavaScript,但如果你必须做重定向,最好用标准的 3xxHTTP 状态码,主要是为了让返回按钮能正确使用。 重定向会拖慢用户体验,在用户和 HTML 文档之间插入重定向会延迟页面上所有对象,页面无法渲染,组件也无法开始下载,直到 HTML 文档被送达浏览器。 如果 URL 尾部缺少/,会重定向到/,例如:/a直接打开,其实是/a/的 301。在 Apache 中可以用Aliasmod_rewriteDirectorySlash指令来取消不必要的重定向。 重定向最常见的用途是把旧网站链接到新的站点,还可以连接同一站点的不同部分,针对用户不同情况(浏览器类型,用户账号类型等等)做出一些处理。用重定向来链接两个网站是最简单的,但降低了用户的体验。一种替代方案是用Aliasmod_rewrite,前提是两个代码路径都在同一个服务器上。 如果因为域名变化而使用了重定向,就可以创建一条CNAME(创建一个指向另一个域名的 DNS 记录作为别名)结合 Alias 或 mod_rewrite 指令

# 缓存 Ajax 请求

最重要的提高 Ajax 性能的方法是让响应变的可缓存

  • Gzip 组件
  • 减少 DNS 查询
  • 压缩 JS
  • 避免重定向
  • 配置 ETags
  • Web2.0 Web3.0

# 延迟加载组件

页面初始加载时,哪些内容不是必需的?不在答案之列的资源可以延迟加载。比如:

  • 非首屏使用的数据、样式、脚本、图片等
  • 用户交互时才会显示的内容
  • 将首屏以外的 HTML 放在不渲染的元素中,如隐藏的<textarea>,或 type 实现为非执行脚本的<script>标签中,减少初始渲染的 DOM 元素数量,提高速度。等首屏加载完或用户操作时,再去渲染剩余的页面内容

# 预加载

预先加载利用浏览器空闲时间请求将来要使用的资源,以便用户访问下一个页面时更快的响应。

  • 无条件预先加载:页面加载完成(load)后,马上获取其他资源。
  • 有条件预加载:根据用户行为判断用户去向(搜索)
  • 有预谋的预先加载:页面即将上线新的版本前预先加载新版本内容。网站改版后由于缓存、使用习惯等原因,会有旧版本的网站更快更流畅的反馈。
<link rel="prefetch" href="sprite.png" />
<link
  rel="prefetch alternate stylesheet"
  title="Designed for Mozilla"
  href="mozspecific.css"
/>
<link rel="next" href="2.html" />

# 减少 DOM 元素数量

从以下几点考虑移除不必要的标记

  • 是否还在使用表格布局
  • 塞进去更多的<div>仅为了处理布局?有没有更好的,更语义化的标记
  • 能通过伪元素实现的功能,就没有必要添加额外元素,如清除浮动

document.getElementsByTagName('*').length来计算页面中有多少 DOM 元素

为什么不使用表格布局

  • 更多的标签,增加文件大小
  • 不易维护,无法适应响应式设计
  • 性能考量,默认表格布局算法会产生大量重绘

# 划分内容到不同域名

浏览器一般会限制每个域的并行线程(一般为 6 个,甚至更少),使用不同的域名可以最大化下载线程,但注意保持在 2-4 个域名内,以避免 DNS 查询损耗。 比如:动态内容放在csspod.com上,静态资源放在static.csspod.com上。这样还可以禁用静态资源下的Cookie,减少数据传输。

# 尽量减少 iframe 的使用

用 iframe 可以把一个 HTML 文档插入到父文档里,重要的是明白 iframe 是如何工作的并高效地使用它。 <iframe>优点

  • 可以用来加载速度较慢的第三方资源,比如广告、徽章
  • 可用作安全沙箱
  • 可并行下载脚本

<iframe>的缺点

  • 加载代价昂贵,即使是空的页面
  • 阻塞页面 load 事件触发
  • 缺乏语义
  • iframe 完全加载完,父页面才会触发 load 事件。safari、Chrome 中通过 JavaScript 动态设置 iframe src 可以避免这个问题。

# 避免 404 错误

# 服务器

  • 使用 CDN
  • 添加 Expires 或 Cache-Control 响应头
  • 启用 Gzip
  • 配置 Etag
  • 尽早输出缓冲
  • Ajax 请求使用 Get 方法
  • 避免图片 src 为空

# 使用 CDN

用户与服务器的物理距离对响应时间也有影响。把内容部署在多个地理位置分散的服务器上能让用户更快的载入页面。 网站 80-90%响应时间消耗在资源下载上,减少资源下载时间是性能优化的黄金法则。相对比分布式架构的复杂和巨大投入,静态内容分发网络(CDN)可以以较低的投入,获得加速速度的有效提升。 内容分发网络(CDN)是一组分散在不同地理位置的 web 服务器,用来给用户更高效地发送内容。典型的,选择用来发送内容的服务器是基于网络距离的衡量标准的。例如:选跳数(hop)最少的或响应时间最快的服务器。

# 添加 Expires 或 Cache-Control 响应头

  • 静态内容:将 Expires 响应头设置为将来很远的时间,实现永不过期策略
  • 动态内容:设置合适的 Cache-control 响应头,让浏览器有条件地发起请求

# 启动 Gzip

web 客户端: Accept-Encoding: gzip, deflate web 服务器通过Content-Encoding响应头来通知客户端: Content-Encoding: gzip

# 配置 Etag

实体标签(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 个字节的响应体。Etag 通过文件版本标识,方便服务器判断请求内容是否有更新,如果没有就响应 304,避免重新下载。

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

# 尽早输出(flush)缓冲

用户请求页面时,服务器通常需要花费200~500毫秒来组合 HTML 页面,在此期间,浏览器处于空闲、等待数据状态。使用 PHP 中的flush()函数,可以发送部分已准备好的 HTML 到浏览器,以便服务器还在忙于处理剩余页面时,浏览器可以提前获取资源。 可以考虑在</head>之后输出一次缓冲,HTML head 一般比较容易生成,先发送以便浏览器开始获取<head>里引用的 css 等资源

<head>
</head>
<?php flush(); ?>
<body>
</body>

# Ajax 请求使用 GET 方法

浏览器执行 XMLHttpRequest Post 请求分两步,先发送 Http Header,再发送 data。而 GET 只使用一个 TCP 数据包(Http Header 与 data)发送数据,所以首选 GET 方法。 根据 HTTP 规范,GET 用于获取数据,POST 则用于向服务器发送数据,所以 Ajax 请求数据时使用 GET 更符合规范

# 避免图片 src 为空

src 属性为空的字符串,浏览器仍然会向服务器发起一个 HTTP 请求:

  • IE 向页面所在的目录发送请求
  • Safari、Chrome、Firefox 向页面本身发送请求
  • Opera 不执行任何操作

后果:

  • 给服务器造成意外的流量负担,尤其是当 PV 较大的时候
  • 浪费服务器计算资源
  • 可能产生错误

空的 href 也有类似问题,浏览器也会向服务器发送 HTTP 请求,可以通过 JavaScript 阻止空链接的默认行为

  • 减少 Cookie 大小
  • 静态资源使用无 cookie 域名

Cookie 被用于身份认证、个性化设置等。Cookie 通过 HTTP 头在服务器和浏览器间来回传送,减少 Cookie 大小可以降低其对响应速度的影响

  • 去除不必要的 Cookie
  • 尽量压缩 Cookie 大小
  • 注意设置 Cookie 的 domain 级别,如无必要,不要影响到 sub-domain;
  • 设置合适的过期时间

静态资源一般无需使用 Cookie,可以把它们放在使用二级域名或专门域名的无 cookie 服务器上,降低 Cookie 传送的造成的流量浪费,提高响应速度

# 移动端

  • 保持单个文件小于 25kb
  • 打包内容为分段 multipart 文档

# 图片

  • 优化图片
  • 优化 css Sprite
  • 不要在 HTML 中缩放图片
  • 使用体积小、可缓存的 favicon.ico
  • 对于较新的浏览器,可以使用 png 格式的 favicon

# 不要在 HTML 中缩放图片

设置的图片宽和高,浏览器会按照猜测的宽高给图片保留的区域和实际宽高差异,产生重绘

# 使用体积小、可缓存的 favicon.ico

  • 存在(避免 404)
  • 尽量小,最好小于 1k
  • 设置较长的过期时间

# JavaScript

  • 把脚本放到页面底部
  • 使用外部 JavaScript 和 css
  • 压缩 JavaScript 和 css
  • 移除重复脚本
  • 减少 DOM 操作
  • 使用高效的事件处理

# CSS

  • 把样式表在<head>
  • 不要使用 css 表达式
  • 使用<link>代替 @import
  • 不要使用 filter