前端性能优化

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

  • 来自 Google 的数据代表,一个有 10 条数据 0.4 秒能加载完的页面,变成 30 条数据 0.9 秒加载完以后,流量和广告收入降低 90%。
  • Google Map 首页文件大小从 100KB 减少到 70-80KB 后,流量在第一周涨了 10%,接下来的三周涨了 25%。
  • 亚马逊的数据代表:加载时间增长 100 毫秒,销量就降低 1%。
    以上数据更说明「加载时间就是金钱」,前端优化主要围绕提升加载速度进行。

1. 页面内容

1.1 减小 HTTP 请求数

Web 前端 80% 的响应时间花在图片、样式、脚本等资源下载上。浏览器对每一个域名的链接数是有限制的,减小请求次数是缩短响应时间的关键。
经过简洁的设计减小页面所需资源,进而减小 HTTP 请求,这是最直接的方式,前提是你的 Boss、设计师同事不打死你。因此,仍是另辟蹊径吧:
合并 JavaScript、CSS 等文件;服务器端(CDN)自动合并。
基于 Node.js 的文件合并工具一抓一大把
使用CSS Sprite:将背景图片合并成一个文件,经过background-imagebackground-position 控制显示;
Sprite Cow
Spritebox
逐步被 Icon Font 和 SVG Sprite 取代。
Image Map:合并图片,而后使用坐标映射不一样的区域。
缺点:仅适用于相连的图片;设置坐标过程乏味且易出错;可访性问题。不推荐使用这种过期的技术。
Inline Assets:使用 Data URI scheme 将图片嵌入 HTML 或者 CSS 中;或者将 CSS、JS、图片直接嵌入 HTML 中。
会增长文件大小,也可能产生浏览器兼容及其余性能问题(有待整理补充)。
将来的趋势是使用内嵌 SVG。
内容分片,将请求划分到不一样的域名上。
HTTP/2 经过多路复用大幅下降了多个请求的开销。经过数据分帧层,客户端和服务器之间只须要创建一个 TCP 链接,便可同时收发多个文件,并且,该链接在至关长的时间周期内保持打开(持久化),以便复用。
HTTP/2 的新特性意味着上述优化实践再也不适用,但考虑到客户端对 HTTP/2 的支持覆盖程度,还需根据实际数据权衡。php

1.2 减小 DNS 查询

用户输入 URL 之后,浏览器首先要查询域名(hostname)对应服务器的 IP 地址,通常须要耗费 20-120 毫秒 时间。DNS 查询完成以前,浏览器没法从服务器下载任何数据。
基于性能考虑,ISP、局域网、操做系统、浏览器都会有相应的 DNS 缓存机制。css

  • IE 缓存 30 分钟,能够经过注册表中 DnsCacheTimeout 项设置;
  • Firefox 混存 1 分钟,经过 network.dnsCacheExpiration 配置;
  • (TODO:补充其余浏览器缓存信息)
    首次访问、没有相应的 DNS 缓存时,域名越多,查询时间越长。因此应尽可能减小域名数量。但基于并行下载考虑,把资源分布到 2 个域名上(最多不超过 4 个)。这是减小 DNS 查询同时保证并行下载的折衷方案。

1.3 避免重定向

HTTP 重定向经过 301/302 状态码实现。html

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

客户端收到服务器的重定向响应后,会根据响应头中 Location 的地址再次发送请求。重定向会影响用户体验,尤为是屡次重定向时,用户在一段时间内看不到任何内容,只看到浏览器进度条一直在刷新。
有时重定向没法避免,在糟糕也比抛出 404 好。虽然经过 HTML meta refresh 和 JavaScript 也能实现,但首选 HTTP 3xx 跳转,以保证浏览器「后退」功能正常工做(也利于 SEO)。
最浪费的重定向常常发生、并且很容易被忽略:URL 末尾应该添加 / 但未添加。好比,访问 http://astrology.yahoo.com/astrology 将被 301 重定向到 http://astrology.yahoo.com/astrology/(注意末尾的 /)。若是使用 Apache,能够经过 Alias 或 mod_rewrite 或 DirectorySlash 解决这个问题。
网站域名变动:CNAME 结合 Alias 或 mod_rewrite 或者其余服务器相似功能实现跳转。前端

1.4 缓存 Ajax 请求

Ajax 能够提升用户体验。但「异步」不意味着「及时」,优化 Ajax 响应速度提升性能还是须要关注的主题。最重要的的优化方式是缓存响应结果,详见 添加 Expires 或 Cache-Control 响应头。
如下规则也关乎 Ajax 响应速度:java

  • 启用 Gzip
  • 减小 DNS 查询
  • 压缩 JavaScript 和 CSS
  • 避免重定向
  • 配置 Etag

1.5 延迟加载

页面初始加载时哪些内容是绝对必需的? 不在答案之列的资源均可以延迟加载。好比:非首屏使用的数据、样式、脚本、图片等;用户交互时才会显示的内容。
遵循「渐进加强」理念开发的网站:JavaScript 用于加强用用户体验,但没有(不支持) JavaScript 也能正常工做,彻底能够延迟加载 JavaScript。
延迟渲染
将首屏之外的 HTML 放在不渲染的元素中,如隐藏的 <textarea>,或者 type 属性为非执行脚本的 <script> 标签中,减小初始渲染的 DOM 元素数量,提升速度。等首屏加载完成或者用户操做时,再去渲染剩余的页面内容。git

1.6 预先加载

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

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

1.7 减小 DOM 元素数量

复杂的页面不只下载的字节更多,JavaScript DOM 操做也更慢。例如,同是添加一个事件处理器,500 个元素和 5000 个元素的页面速度上会有很大区别。
从如下几个角度考虑移除没必要要的标记:ajax

  • 是否还在使用表格布局?
  • 塞进去更多的
    仅为了处理布局问题?也许有更好、更语义化的标记。
  • 能经过伪元素实现的功能,就不必添加额外元素,如清除浮动。
    浏览器控制台中输入如下代码能够计算出页面中有多少 DOM 元素:document.getElementsByTagName('*').length; 对比标记良好的的网站,看看差距是多少。
    为何不使用表格布局?
    更多的标签,增长文件大小;不易维护,没法适应响应式设计;性能考量,默认的表格布局算法会产生大量重绘(参见表格布局算法)。

1.8 划份内容到不一样域名

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

1.9 尽可能减小 iframe 使用

使用 iframe 能够在页面中嵌入 HTML 文档,但有利有弊。
<iframe> 优势:
能够用来加载速度较慢的第三方资源,如广告、徽章;
可用做安全沙箱;
能够并行下载脚本。
<iframe> 缺点:
加载代价昂贵,即便是空的页面;
阻塞页面 load 事件触发;
Iframe 彻底加载之后,父页面才会触发 load 事件。 Safari、Chrome 中经过 JavaScript 动态设置 iframe src 能够避免这个问题。
缺少语义。

1.10 避免 404 错误

HTTP 请求很昂贵,返回无效的响应(如 404 未找到)彻底不必,下降用户体验并且毫无益处。
一些网站设计很酷炫、有提示信息的 404 页面,有助于提升用户体验,但仍是浪费服务器资源。尤为糟糕的是外部脚本返回 404,不只阻塞其余资源下载,浏览器还会尝试把 404 页面内容看成 JavaScript 解析,消耗更多资源。
补充规则:
定义字符集,并放在 顶部。大多数浏览器会暂停页面渲染,直到找到字符集定义。

2. 服务器

2.1 尽早输出(flush)缓冲

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

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

2.2 Ajax 请求使用 GET 方法

浏览器执行 XMLHttpRequest POST 请求时分红两步,先发送 Header,再发送数据。而 GET 只使用一个 TCP 数据包发送数据,因此首选 GET 方法。
根据 HTTP 规范,GET 用于获取数据,POST 则用于向服务器发送数据,因此 Ajax 请求数据时使用 GET 更符合规范(GET 和 POST 对比)。
IE 中最大 URL 长度为 2K,若是超出 2K,则须要考虑使用 POST 方法。

2.3 避免图片 src 为空

图片 src 属性值为空字符串可能如下面两种形式出现:
HTML:
<img src="" />
JavaScript:

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

虽然 src 属性为空字符串,但浏览器仍然会向服务器发起一个 HTTP 请求:
IE 向页面所在的目录发送请求;
Safari、Chrome、Firefox 向页面自己发送请求;
Opera 不执行任何操做。
以上数据较老,当下主流版本可能会有改变。
空 src 产生请求的后果不容小觑:
给服务器形成意外的流量负担,尤为时日 PV 较大时;
浪费服务器计算资源;
可能产生报错。
固然,浏览器如此实现也是根据 RFC 3986 - Uniform Resource Identifiers,当空字符串做为 URI 出现时,被当成相对 URI,具体算法参见规范 5.2 节。
空的 href 属性也存在相似问题。用户点击空连接时,浏览器也会向服务器发送 HTTP 请求,能够经过 JavaScript 阻止空连接的默认的行为。

Cookie 被用于身份认证、个性化设置等诸多用途。Cookie 经过 HTTP 头在服务器和浏览器间来回传送,减小 Cookie 大小能够下降其对响应速度的影响。
去除没必要要的 Cookie;
尽可能压缩 Cookie 大小;
注意设置 Cookie 的 domain 级别,如无必要,不要影响到 sub-domain;
设置合适的过时时间。
HTTP/2 首部压缩在客户端和服务器端使用「首部表」来跟踪和存储以前发送的键值对,对于相同的数据,再也不随每次请求和响应发送。

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

2.5 CSS

把样式表放在 中
把样式表放在 中可让页面渐进渲染,尽早呈现视觉反馈,给用户加载速度很快的感受。
这对内容比较多的页面尤其重要,用户能够先查看已经下载渲染的内容,而不是盯着白屏等待。
若是把样式表放在页面底部,一些浏览器为减小重绘,会在 CSS 加载完成之后才渲染页面,用户只能对着白屏干瞪眼,用户体验极差。
不要使用 CSS 表达式
CSS 表达式能够在 CSS 里执行 JavaScript,仅 IE5-IE7 支持,IE8 标准模式已经废弃。
CSS 表达式超出预期的频繁执行,页面滚动、鼠标移动时都会不断执行,带来很大的性能损耗。
IE7 及更低版本的浏览器已经逐渐成为历史,忘记它吧。
使用 <link> 替代 @import
对于 IE 某些版本,@import 的行为和 <link> 放在页面底部同样。因此,不要用它。
不要使用 filter
AlphaImageLoader 为 IE5.5-IE8 专有的技术,和 CSS 表达式同样,放进博物馆吧。
_注意:_这里所说的不是 CSS3 Filter

2.6 JavaScript

把脚本放在页面底部
浏览器下载脚本时,会阻塞其余资源并行下载,即便是来自不一样域名的资源。所以,最好将脚本放在底部,以提升页面加载速度。
一些特殊场景没法将脚本放到页面底部的,能够考虑 <script> 的如下属性:

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

2.7 使用外部 JavaScript 和 CSS

外部 JavaScript 和 CSS 文件能够被浏览器缓存,在不一样页面间重用,也能下降页面大小。
固然,实际中也须要考虑代码的重用程度。若是仅仅是某个页面使用到的代码,能够考虑内嵌在页面中,减小 HTTP 请求数。另外,能够在首页加载完成之后,预先加载子页面的资源。

2.8 压缩 JavaScript 和 CSS

压缩代码能够移除非功能性的字符(注释、空格、空行等),减小文件大小,提升载入速度。
得益于 Node.js 的流行,开源社区涌现出许多高效、易用的前端优化工具,JavaScript 和 CSS 压缩类的,不敢说多如牛毛,多入鸡毛却是一点不夸张,如 [UglifyJS 2] (https://github.com/mishoo/UglifyJS2)、csso、cssnano 等。
对于内嵌的 CSS 和 JavaScript,也能够经过 htmlmin 等工具压缩。
这些项目都有 Gulp、Webpack 等流行构建工具的配套版本。

2.9 移除重复脚本

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

2.10 减小 DOM 操做

JavaScript 操做 DOM 很慢,尤为是 DOM 节点不少时。
使用时应该注意:

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

2.11 使用高效的事件处理

减小绑定事件监听的节点,如经过事件委托;
尽早处理事件,在 DOMContentLoaded 便可进行,不用等到 load 之后。
对于 resize、scroll 等触发频率极高的事件,应该经过 debounce 等机制下降处理程序执行频率。
TODO: 补充相关内容

2.12 图片

优化图片
YDN 列出的相关工具 缺少易用性,建议参考如下工具。
imagemin
ImageOptim
TODO:
PNG 终极优化;
Webp 相关内容;
SVG 相关内容。
PNG 终极优化:

2.13 优化 CSS Sprite

水平排列 Sprite 中的图片,垂直排列会增长图片大小;
Spirite 中把颜色较近的组合在一块儿能够下降颜色数,理想情况是低于 256 色以适用 PNG8 格式;
不要在 Spirite 的图像中间留有较大空隙。减小空隙虽然不太影响文件大小,但能够下降用户代理把图片解压为像素图的内存消耗,对移动设备更友好。
不要在 HTML 中缩放图片
不要使用 的 width、height 缩放图片,若是用到小图片,就使用相应大小的图片。
不少 CMS 和 CDN 都提供图片裁切功能。

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

Favicon.ico 通常存放在网站根目录下,不管是否在页面中设置,浏览器都会尝试请求这个文件。
因此确保这个图标:

  • 存在(避免 404);
  • 尽可能小,最好小于 1K;
  • 设置较长的过时时间。
    对于较新的浏览器,可使用 PNG 格式的 favicon。

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

2.15 移动端

移动端优化相关内容有待进一步整理补充。

相关文章
相关标签/搜索