雅虎35条军规
页面内容
减少HTTP请求
客户端80%的时间花在图片、样式、脚本等资源下载上,最直接的方式是减少页面所需资源,但是并不现实,但是可以通过压缩合并来减少HTTP请求数。
- 合并JS/CSS文件,服务端CDN自动合并,构建工具合并等。
- 使用CSS Sprite,一些控件、背景图片等都可以合并,通过
background-image
和background-position
来控制。 - 小型图片可以使用Base64编码,使用Data URI scheme将图片嵌入HTML或者CSS中;或者将CSS、JS、image直接嵌入HTML中,但会增加文件大小,也可能产生浏览器兼容及其他性能问题。
减少页面的HTTP请求数是个起点,这是提升站点首次访问速度的重要知道原则。
减少DNS查询
用户输入URL后,都会通过DNS查询对应服务器的IP地址,一般需要消耗20~120ms。DNS查询完成之前,浏览器无法从服务器下载任何数据。
基于性能考虑,ISP、局域网、操作系统、浏览器都会有相应的DNS缓存机制。
另外减少不同的主机名可减少DNS查找的时间,减少不同主机名的数量同时也减少了页面能够并行下载的组件数量,避免DNS查找削减了响应时间,而减少并行下载数量却增加了响应时间。原则上是把组件分散2到4个主机名下,这是同时减少DNS查询和允许高并发下载的折中方案。
避免重定向
HTTP重定向通过301/302状态码实现。
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html
浏览器自动跳转Location域指明的URL。重定向需要的信息都在HTTP首部,而响应体一般是空的。其实额外的HTTP首部,比如Expires和Cache-Control也表示重定向。除此之外还有别的跳转方式,refresh和JavaScript,如果必须做重定向,最好使用标准的3xx系列状态码。
客户端受到服务器的重定向响应后,会根据响应首部中的Location再次发送请求,尤其在多次重定向时,用户在一段时间内看不到任何内容,只看到浏览器在一直刷新等待。
缓存Ajax请求
如果有尚未过期的Expires或者Cache-Control首部字段,那么之前的资源就能从缓存中读取。
详情请参考HTTP首部Expires和Cache-Control首部字段。
延迟加载
页面初始加载时,一些不需要立即加载的资源都可以延迟加载。如:
- 非首屏使用的数据、样式、脚本、图片等。
- 用户交互时才会需要的资源。
遵循「渐进增强」理念开发的网站:JavaScript用于增强用用户体验,但没有(不支持) JavaScript也能正常工作,完全可以延迟加载JavaScript。
预加载
- 无条件预加载:页面onload后,马上获取其他资源。
- 有条件预加载:根据用户行为预判用户去向,预加载相关资源。
- 有“目的”的预加载:页面即将上线新版内容,新版内容所需要的资源可以预先在旧版本中缓存,以便新版本上线更快的载入。
减少DOM元素数量
- 是否还在使用
table
- 更多的
div
是否真的需要? - 复杂的选择器不如一个
class
简单直接有效 - 能通过伪元素实现的功能,就没必要添加额外元素。
document.getElementsByTagName('*').length;
上述代码可以获得页面中的DOM元素数量。
划分内容到不同域名
HTTP/1.1中,每个域名有并行数限制,使用不同的域名可以最大化下载线程,但注意不可过多分配域名,避免DNS查询消耗。
减少iframe的使用
用iframe可以把一个HTML文档插入到父文档中。
iframe
的优点:
- 可以加载速度慢的第三方资源,如广告、徽章。
- 用作安全沙箱
- 并行加载脚本。
iframe
的缺点:
- 加载代价昂贵,即使是空页面
- 阻塞页面load事件触发。
Iframe 完全加载以后,父页面才会触发 load 事件。 Safari、Chrome 中通过 JavaScript 动态设置 iframe src 可以避免这个问题。
服务器
启用CDN
用户与服务器的物理距离对响应时间的影响显而易见,把内容部署在多个地理位置分布分散的服务器可以让用户更快的载入页面。
网站80%~90%的时间消耗在资源加载上,使用CDN(内容分发网络)可以以低投入(相对)获得加载速度和收益的有效提升。
CDN是一组分散在不同地理位置的web服务器,用来给用户更高效地发送内容。典型地,选择用来发送内容的服务器是基于网络距离的衡量标准的。例如:选跳数(hop)最少的或者响应时间最快的服务器。
添加Expires和Cache-Control响应头
- 静态内容:将 Expires 响应头设置为将来很远的时间,实现「永不过期」策略;
- 动态内容:设置合适的 Cache-Control 响应头,让浏览器有条件地发起请求。
Cache-Control头在HTTP/1.1规范中定义,取代了之前用来定义响应缓存策略的头(例如 Expires、Pragma)。当前的所有浏览器都支持Cache-Control,因此,使用它就够了。
启用Gzip
内容编码压缩可以明显缩短网络传输时间。
Gzip压缩通常可以减少70%的响应大小,对某些文件可能高达90%,对HTML、CSS、JS等text文件内容启用内容编码压缩。
注意!!! 图片和 PDF 文件不要使用 gzip。它们本身已经压缩过,再使用 gzip 压缩不仅浪费 CPU 资源,而且还可能增加文件体积。
从HTTP/1.1开始,web客户端就有了支持压缩的Accept-Encoding HTTP请求头。
Accept-Encoding: gzip, deflate
如果web服务器看到这个请求头,它就会用客户端列出的一种方式来压缩响应。web服务器通过Content-Encoding响应头来通知客户端。
Content-Encoding: gzip
配置ETag
实体标签(ETags),是服务器和浏览器用来决定浏览器缓存中组件与源服务器中的组件是否匹配的一种机制(“实体”也就是组件:图片,脚本,样式表等等)。添加ETags可以提供一种实体验证机制,比最后修改日期更加灵活。一个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 通过文件版本标识,方便服务器判断请求的内容是否有更新。
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
尽早输出缓冲
用户请求页面时,服务器需要花费200~500ms来组合HTML页面。在此期间,浏览器处于空闲、等待数据状态。使用PHP的flush函数,可以发送部分已经准备好的HTML给浏览器,以便服务器忙于处理剩余页面时,浏览器可以提前加载解析资源。
Ajax使用GET方法
浏览器执行XMLHttpRequest POST请求时分成两步,先发送Http Header,再发送data。而GET只使用一个TCP数据包(Http Header与data)发送数据,所以首选GET方法。
根据HTTP规范,GET用于获取数据,POST则用于向服务器发送数据,所以Ajax请求数据时使用GET更符合规范。
避免图片src为空
<img src="" />
OR
var img = new Image();
img.src = "";
虽然src属性为空字符串,但浏览器仍然会向服务器发起一个HTTP请求:
- IE 向页面所在的目录发送请求;
- Safari、Chrome、Firefox向页面本身发送请求;
- Opera不执行任何操作。
空src产生请求的后果不容小觑:
- 给服务器造成意外的流量负担,尤其时日 PV 较大时;
- 浪费服务器计算资源;
- 可能产生报错。
空的href属性也存在类似问题。用户点击空链接时,浏览器也会向服务器发送HTTP请求,可以通过JavaScript阻止空链接的默认的行为。
Cookie
减少 Cookie 大小
Cookie被用于身份认证、个性化设置等诸多用途。Cookie通过HTTP头在服务器和浏览器间来回传送,减少Cookie大小可以降低其对响应速度的影响。
注意设置 Cookie 的 domain 级别,如无必要,不要影响到 sub-domain;
静态资源使用无Cookie域名
静态资源一般无需使用Cookie,可以把它们放在使用二级域名或者专门域名的无Cookie服务器上,降低Cookie传送的造成的流量浪费,提高响应速度。
CSS
把样式表放在<head>中
把样式表放在<head>中可以让页面渐进渲染,尽早呈现视觉反馈,给用户加载速度很快的感觉。
这对内容比较多的页面尤为重要,用户可以先查看已经下载渲染的内容,而不是盯着白屏等待。
如果把样式表放在页面底部,一些浏览器为减少重绘,会在 CSS 加载完成以后才渲染页面,用户只能对着白屏干瞪眼,用户体验极差。把样式表放到文档的HEAD部分能让页面看起来加载地更快。
不要使用CSS表达式
CSS表达式可以在CSS里执行JavaScript,仅IE5-IE7支持,IE8标准模式已经废弃。 CSS表达式超出预期的频繁执行,页面滚动、鼠标移动时都会不断执行,带来很大的性能损耗。
使用<link>替代@import
对于IE某些版本,@import的行为和放在页面底部一样。所以,不要用它。
JavaScript
把脚本放在页面底部
浏览器下载脚本时,会阻塞其他资源并行下载,即使是来自不同域名的资源。因此,最好将脚本放在底部,以提高页面加载速度。
一些特殊场景无法将脚本放到页面底部的,可以考虑<script>的以下属性:
defer
- HTML5的
async
`
使用外部JavaScript和CSS
外部JavaScript和CSS文件可以被浏览器缓存,在不同页面间重用,也能降低页面大小。
当然,实际中也需要考虑代码的重用程度。如果仅仅是某个页面使用到的代码,可以考虑内嵌在页面中,减少HTTP请求数。另外,可以在首页加载完成以后,预先加载子页面的资源。
压缩JavaScript和CSS
压缩代码可以移除非功能性的字符(注释、空格、空行等),减少文件大小,提高载入速度。
得益于Node.js的流行,开源社区涌现出许多高效、易用的前端优化工具,JavaScript 和CSS压缩类的,不敢说多如牛毛,多入鸡毛倒是一点不夸张,如[UglifyJS 2] (github.com/mishoo/Ugli…)、csso、cssnano 等。
对于内嵌的CSS和JavaScript,也可以通过htmlmin等工具压缩。
这些项目都有Gulp、Webpack等流行构建工具的配套版本。
移除重复脚本
JavaScript 操作 DOM 很慢,尤其是 DOM 节点很多时。
使用时应该注意:
- 缓存访问过的元素
- 使用DocumentFragment暂存DOM,整理好以后再插入DOM树;
- 操作className,而不是多次读写style
- 避免使用JavaScript修复布局。
使用高效的事件处理
- 减少绑定事件监听的节点,如通过事件委托;
- 尽早处理事件,在DOMContentLoaded即可进行,不用等到load以后。
Image
优化图片
尝试把GIF格式转换成PNG格式,看看是否节省空间。在所有的PNG图片上运行pngcrush(或者其它PNG优化工具)。
CSS Sprite
- 水平排列 Sprite 中的图片,垂直排列会增加图片大小;
- sprite 中把颜色较近的组合在一起可以降低颜色数,理想状况是低于 256 色以适用 PNG8 格式;
- 不要在 Spirite 的图像中间留有较大空隙。减少空隙虽然不太影响文件大小,但可以降低用户代理把图片解压为像素图的内存消耗,对移动设备更友好。
不要在 HTML 中缩放图片
不要使用<img>的 width、height 缩放图片,如果用到小图片,就使用相应大小的图片。如果需要
<img width="100" height="100" src="mycat.jpg" alt="My Cat" />
那么图片本身(mycat.jpg)应该是 100x100px 的,而不是去缩小 500x500px 的图片。
使用体积小、可缓存的 favicon.ico
Favicon.ico 一般存放在网站根目录下,无论是否在页面中设置,浏览器都会尝试请求这个文件。
所以确保这个图标:
- 存在(避免 404);
- 尽量小,最好小于 1K;
- 设置较长的过期时间。
对于较新的浏览器,可以使用 PNG 格式的 favicon。
Mobile
保证所有组件都小于 25K
这个限制是因为 iPhone 不能缓存大于 25K 的组件,注意这里指的是未压缩的大小。这就是为什么缩减内容本身也很重要,因为单纯的 gzip 可能不够。
Keep Components under 25K
打包内容为分段(multipart)文档
把各个组件打包成一个像有附件的电子邮件一样的复合文档里,可以用一个 HTTP 请求获取多个组件(记住一点:HTTP 请求是代价高昂的)。用这种方式的时候,要先检查用户代理是否支持(iPhone 就不支持)。