前端性能的优化

主要考虑客户端性能、服务器端和网络性能,内容框架来自Yahoo Developer Network(雅虎开发者网站),包含 7 个类别共 35 条前端性能优化最佳实践,在此基础上补充了一些相关或者更符合主流技术的内容。javascript

 

前端性能的一个重要指标是页面加载时间,不只事关用户体验,也是搜索引擎排名考虑的一个因素。php

  • 来自Google的数据代表,一个有10条数据0.4秒能加载完的页面,变成30条数据0.9秒加载完以后,流量和广告收入降低90%。
  • Google Map 首页文件大小从100KB减少到70-80KB后,流量在第一周涨了10%,接下来的三周涨了25%。
  • 亚马逊的数据代表:加载时间增长100毫秒,销量就降低1%。

以上数据更说明「加载时间就是金钱」,前端优化主要围绕提升加载速度进行。css

1、页面内容

1. 减小HTTP请求数 Web 前端 80% 的响应时间花在图片、样式、脚本等资源下载上。最直接的方式是减小页面所需资源,但并不现实。因此,减小HTTP请求数主要的途径是:html

  • 合并JS/CSS文件。服务器端(CDN)自动合并,基于Node.js的文件合并工具,经过把全部脚本放在一个文件中的方式来减小请求数。
  • 使用CSS Sprite将背景图片合并成一个文件,经过background-imagebackground-position 控制显示
  • 行内图片(Base64编码)。使用Data URI scheme将图片嵌入HTML或者CSS中;或者将CSS、JS、图片直接嵌入HTML中,会增长文件大小,也可能产生浏览器兼容及其余性能问题。

减小页面的HTTP请求数是个起点,这是提高站点首次访问速度的重要指导原则。前端

2. 减小DNS查询 用户输入URL之后,浏览器首先要查询域名(hostname)对应服务器的IP地址,通常须要耗费20-120毫秒时间。DNS查询完成以前,浏览器没法从服务器下载任何数据。java

基于性能考虑,ISP、局域网、操做系统、浏览器都会有相应的DNS缓存机制。git

  • IE缓存30分钟,能够经过注册表中DnsCacheTimeout项设置;
  • Firefox缓存1分钟,经过network.dnsCacheExpiration配置;

另外减小不一样的主机名可减小DNS查找,减小不一样主机名的数量同时也减小了页面可以并行下载的组件数量,避免DNS查找削减了响应时间,而减小并行下载数量却增长了响应时间。原则是把组件分散在2到4个主机名下,这是同时减小DNS查找和容许高并发下载的折中方案。github

3. 避免重定向 HTTP重定向经过301/302状态码实现。下面是一个有301状态码的HTTP头web

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状态码,主要是为了让返回按钮能正常使用。算法

客户端收到服务器的重定向响应后,会根据响应头中Location的地址再次发送请求。重定向会影响用户体验,尤为是屡次重定向时,用户在一段时间内看不到任何内容,只看到浏览器进度条一直在刷新。

  • 最浪费的重定向常常发生、并且很容易被忽略:URL 末尾应该添加/但未添加。好比,访问http://astrology.yahoo.com/astrology将被301重定向到http://astrology.yahoo.com/astrology/(注意末尾的 /)。若是使用 Apache,能够经过Alias或mod_rewrite或DirectorySlash解决这个问题。
  • 网站域名变动:CNAME结合Alias或mod_rewrite或者其余服务器相似功能实现跳转。

4. 缓存Ajax请求 最重要的的优化方式是缓存响应结果。有还没有过时的Expires或者Cache-Control HTTP头,那么以前的资源就能够从缓存中读出。必须通知浏览器,应该继续使用以前缓存的资源响应,仍是去请求一个新的。能够经过给资源的Ajax URL里添加一个代表用户资源最后修改时间的时间戳来实现。若是资源从上一次下载以后再没有被修改过,时间戳不变,资源就将从浏览器缓存中直接读出,从而避免一次额外的HTTP往返消耗。详见服务器-添加Expires或Cache响应头

5. 延迟加载 页面初始加载时哪些内容是绝对必需的?不在答案之列的资源均可以延迟加载。好比:

  • 非首屏使用的数据、样式、脚本、图片等;
  • 用户交互时才会显示的内容。

遵循「渐进加强」理念开发的网站:JavaScript用于加强用用户体验,但没有(不支持) JavaScript也能正常工做,彻底能够延迟加载JavaScript。

将首屏之外的HTML放在不渲染的元素中,如隐藏的<textarea>,或者type属性为非执行脚本的<script>标签中,减小初始渲染的DOM元素数量,提升速度。等首屏加载完成或者用户操做时,再去渲染剩余的页面内容。

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

  • 无条件预先加载:页面加载完成(load)后,立刻获取其余资源。以 google.com 为例,首页加载完成后会当即下载一个 Sprite 图片,此图首页不须要,可是搜索结果页要用到。
  • 有条件预先加载:根据用户行为预判用户去向,预载相关资源。好比search.yahoo.com开始输入时会有额外的资源加载。Chrome 等浏览器的地址栏也有相似的机制。
  • 有「阴谋」的预先加载:页面即将上线新版前预先加载新版内容。网站改版后因为缓存、使用习惯等缘由,会有旧版的网站更快更流畅的反馈。为缓解这一问题,在新版上线以前,旧版能够利用空闲提早加载一些新版的资源缓存到客户端,以便新版正式上线后更快的载入。

7. 减小DOM元素数量 复杂的页面不只下载的字节更多,JavaScript DOM操做也更慢。例如,同是添加一个事件处理器,500个元素和5000个元素的页面速度上会有很大区别。

从如下几个角度考虑移除没必要要的标记:

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

浏览器控制台中输入如下代码能够计算出页面中有多少 DOM 元素:

document.getElementsByTagName('*').length

为何不使用表格布局?

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

8. 划份内容到不一样域名 浏览器通常会限制每一个域的并行线程(通常为6个,甚至更少),使用不一样的域名能够最大化下载线程,但注意保持在2-4个域名内,以免DNS查询损耗。

例如,动态内容放在csspod.com上,静态资源放在static.csspod.com上。这样还能够禁用静态资源域下的Cookie,减小数据传输,详见Cookie 优化

9. 尽可能减小iframe的使用 用iframe能够把一个HTML文档插入到父文档里,重要的是明白iframe是如何工做的并高效地使用它。

<iframe>的优势:

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

<iframe>的缺点:

  • 加载代价昂贵,即便是空的页面;
  • 阻塞页面 load 事件触发;

Iframe 彻底加载之后,父页面才会触发 load 事件。 Safari、Chrome 中经过 JavaScript 动态设置 iframe src 能够避免这个问题。

  • 缺少语义。

10. 避免404错误 HTTP请求很昂贵,返回无效的响应(如404未找到)彻底不必,下降用户体验并且毫无益处。 一些网站设计很酷炫、有提示信息的404页面,有助于提升用户体验,但仍是浪费服务器资源。尤为糟糕的是外部脚本返回404,不只阻塞其余资源下载,浏览器还会尝试把404页面内容看成JavaScript解析,消耗更多资源。

 

2、服务器

1. 使用CDN 用户与服务器的物理距离对响应时间也有影响。把内容部署在多个地理位置分散的服务器上能让用户更快地载入页面。但具体要怎么作呢?

网站80-90%响应时间消耗在资源下载上,减小资源下载时间是性能优化的黄金法则。相比分布式架构的复杂和巨大投入,静态内容分发网络(CDN)能够以较低的投入,得到加载速度有效提高。

内容分发网络(CDN)是一组分散在不一样地理位置的web服务器,用来给用户更高效地发送内容。典型地,选择用来发送内容的服务器是基于网络距离的衡量标准的。例如:选跳数(hop)最少的或者响应时间最快的服务器。

2. 添加Expires或Cache-Control响应头

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

Cache-Control头在HTTP/1.1规范中定义,取代了以前用来定义响应缓存策略的头(例如 Expires、Pragma)。当前的全部浏览器都支持Cache-Control,所以,使用它就够了。

3. 启用Gzip 前端工程师能够想办法明显地缩短经过网络传输HTTP请求和响应的时间。毫无疑问,终端用户的带宽速度,网络服务商,对等交换点的距离等等,都是开发团队所没法控制的。但还有别的可以影响响应时间的因素,压缩能够经过减小HTTP响应的大小来缩短响应时间。

Gzip压缩一般能够减小70%的响应大小,对某些文件更可能高达90%,比Deflate更高效。主流 Web 服务器都有相应模块,并且绝大多数浏览器支持gzip解码。因此,应该对HTML、CSS、JS、XML、JSON等文本类型的内容启用压缩。

注意!!! 图片和 PDF 文件不要使用 gzip。它们自己已经压缩过,再使用 gzip 压缩不只浪费 CPU 资源,并且还可能增长文件体积。

从HTTP/1.1开始,web客户端就有了支持压缩的Accept-Encoding HTTP请求头。

Accept-Encoding: gzip, deflate

若是web服务器看到这个请求头,它就会用客户端列出的一种方式来压缩响应。web服务器经过Content-Encoding响应头来通知客户端。

Content-Encoding: gzip

 

4. 配置 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 

 

5. 尽早输出(flush)缓冲 用户请求页面时,服务器一般须要花费200 ~ 500毫秒来组合 HTML 页面。在此期间,浏览器处于空闲、等待数据状态。使用PHP中的flush()函数,能够发送部分已经准备好的 HTML到浏览器,以便服务器还在忙于处理剩余页面时,浏览器能够提早开始获取资源。

能够考虑在</head>以后输出一次缓冲,HTML head通常比较容易生成,先发送以便浏览器开始获取<head>里引用的CSS等资源。

<!-- css, js --> </head> <?php flush(); ?> <body> <!-- content --> 

6. Ajax请求使用GET方法 浏览器执行XMLHttpRequest POST请求时分红两步,先发送Http Header,再发送data。而GET只使用一个TCP数据包(Http Header与data)发送数据,因此首选GET方法。

根据HTTP规范,GET用于获取数据,POST则用于向服务器发送数据,因此Ajax请求数据时使用GET更符合规范。

7. 避免图片src为空 图片src属性值为空字符串可能如下面两种形式出现:

HTML:

<img src="" /> 

JavaScript:

var img = new Image(); img.src = ""; 

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

  • IE 向页面所在的目录发送请求;
  • Safari、Chrome、Firefox向页面自己发送请求;
  • Opera不执行任何操做。

空src产生请求的后果不容小觑:

  • 给服务器形成意外的流量负担,尤为时日 PV 较大时;
  • 浪费服务器计算资源;
  • 可能产生报错。

空的href属性也存在相似问题。用户点击空连接时,浏览器也会向服务器发送HTTP请求,能够经过JavaScript阻止空连接的默认的行为。

 

3、Cookie

1. 减小 Cookie 大小 Cookie被用于身份认证、个性化设置等诸多用途。Cookie经过HTTP头在服务器和浏览器间来回传送,减小Cookie大小能够下降其对响应速度的影响。

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

2. 静态资源使用无Cookie域名 静态资源通常无需使用Cookie,能够把它们放在使用二级域名或者专门域名的无Cookie服务器上,下降Cookie传送的形成的流量浪费,提升响应速度。

 

4、CSS

1. 把样式表放在<head>中 把样式表放在<head>中可让页面渐进渲染,尽早呈现视觉反馈,给用户加载速度很快的感受。

这对内容比较多的页面尤其重要,用户能够先查看已经下载渲染的内容,而不是盯着白屏等待。

若是把样式表放在页面底部,一些浏览器为减小重绘,会在 CSS 加载完成之后才渲染页面,用户只能对着白屏干瞪眼,用户体验极差。把样式表放到文档的HEAD部分能让页面看起来加载地更快。

2. 不要使用CSS表达式 CSS表达式能够在CSS里执行JavaScript,仅IE5-IE7支持,IE8标准模式已经废弃。 CSS表达式超出预期的频繁执行,页面滚动、鼠标移动时都会不断执行,带来很大的性能损耗。

3. 使用<link>替代@import 对于IE某些版本,@import的行为和放在页面底部同样。因此,不要用它。

4. 不要使用 filter AlphaImageLoader为IE5.5-IE8专有的技术,和CSS表达式同样,放进博物馆吧。IE专有的AlphaImageLoader滤镜能够用来修复IE7以前的版本中半透明PNG图片的问题。在图片加载过程当中,这个滤镜会阻塞渲染,卡住浏览器,还会增长内存消耗并且是被应用到每一个元素的,而不是每一个图片,因此会存在一大堆问题。

注意!!!这里所说的不是 CSS3 Filter

 

5、Javasript

1. 把脚本放在页面底部 浏览器下载脚本时,会阻塞其余资源并行下载,即便是来自不一样域名的资源。所以,最好将脚本放在底部,以提升页面加载速度。

一些特殊场景没法将脚本放到页面底部的,能够考虑<script>的如下属性:

  • defer 属性;
  • HTML5 新增的async属性。

2. 使用外部JavaScript和CSS 外部JavaScript和CSS文件能够被浏览器缓存,在不一样页面间重用,也能下降页面大小。

固然,实际中也须要考虑代码的重用程度。若是仅仅是某个页面使用到的代码,能够考虑内嵌在页面中,减小HTTP请求数。另外,能够在首页加载完成之后,预先加载子页面的资源。

3. 压缩JavaScript和CSS 压缩代码能够移除非功能性的字符(注释、空格、空行等),减小文件大小,提升载入速度。

得益于Node.js的流行,开源社区涌现出许多高效、易用的前端优化工具,JavaScript 和CSS压缩类的,不敢说多如牛毛,多入鸡毛却是一点不夸张,如[UglifyJS 2] (github.com/mishoo/Ugli…)、csso、cssnano 等。

对于内嵌的CSS和JavaScript,也能够经过htmlmin等工具压缩。

这些项目都有Gulp、Webpack等流行构建工具的配套版本。

4. 移除重复脚本 重复的脚本不只产生没必要要的HTTP请求,并且重复解析执行浪费时间和计算资源。

5. 减小DOM操做 JavaScript 操做 DOM 很慢,尤为是 DOM 节点不少时。

使用时应该注意:

  • 缓存已经访问过的元素;
  • 使用DocumentFragment暂存DOM,整理好之后再插入DOM树;
  • 操做className,而不是屡次读写style;
  • 避免使用JavaScript修复布局。

6. 使用高效的事件处理

  • 减小绑定事件监听的节点,如经过事件委托;
  • 尽早处理事件,在DOMContentLoaded便可进行,不用等到load之后。

 

6、图片

1. 优化图片 尝试把GIF格式转换成PNG格式,看看是否节省空间。在全部的PNG图片上运行pngcrush(或者其它PNG优化工具)。

YDN列出的相关工具缺少易用性,建议参考如下工具

  • imagemin
  • imageoptim.com

TODO:

  • PNG 终极优化;
  • Webp 相关内容;
  • SVG 相关内容。

PNG终极优化

  • Most Effective Method to Reduce and Optimize PNG Images
  • Clever PNG Optimization Techniques

2. 优化CSS Sprite

  • 水平排列Sprite中的图片,垂直排列会增长图片大小;
  • Spirite中把颜色较近的组合在一块儿能够下降颜色数,理想情况是低于256色以适用PNG8格式;
  • 不要在Spirite的图像中间留有较大空隙。减小空隙虽然不太影响文件大小,但能够下降用户代理把图片解压为像素图的内存消耗,对移动设备更友好。

3. 不要在HTML中缩放图片 不要使用<img>的width、height缩放图片,若是用到小图片,就使用相应大小的图片。若是须要

<img width="100" height="100" src="mycat.jpg" alt="My Cat" /> 

那么图片自己(mycat.jpg)应该是100x100px的,而不是去缩小500x500px的图片。

不少 CMS 和 CDN 都提供图片裁切功能。

补充:设置图片的宽和高,以避免浏览器按照「猜」的宽高给图片保留的区域和实际宽高差别,产生重绘。

4. 使用体积小、可缓存的favicon.ico Favicon.ico通常存放在网站根目录下,不管是否在页面中设置,浏览器都会尝试请求这个文件。

因此确保这个图标:

  • 存在(避免 404);
  • 尽可能小,最好小于 1K;
  • 设置较长的过时时间。

对于较新的浏览器,可使用PNG格式的favicon。

 

7、移动端

1. 保证全部组件都小于25K 这个限制是由于iPhone不能缓存大于25K的组件,注意这里指的是未压缩的大小。这就是为何缩减内容自己也很重要,由于单纯的gzip可能不够。

2. 打包内容为分段(multipart)文档 把各个组件打包成一个像有附件的电子邮件同样的复合文档里,能够用一个HTTP请求获取多个组件(记住一点:HTTP请求是代价高昂的)。用这种方式的时候,要先检查用户代理是否支持(iPhone就不支持)。

 

总结

写到这里,雅虎的35条军规算是介绍完了。条目虽然不少,但通过分类,能够发现,性能优化主要切入点能够从如下几个方面去考虑:

  • 资源自己大小的压缩优化(想办法减小资源的体积)
  • 网络请求的全过程(从url地址栏输入发送请求开始到返回响应包的每一个环节)
  • 浏览器渲染的全过程(拿到资源后浏览器渲染的每一个环节) 所以,要完全掌握优化的方法,必须对http请求的全过程以及浏览器的渲染全过程都有深刻的理解。
相关文章
相关标签/搜索