网站性能优化可以从下面总结点入手.
1. 减少 HTTP 请求
使用雪碧图 - CSS Sprites, 把多个图片合并到一个单独的图片中, 利用 CSS -background-position 调整图片显示位置. 这种方式适用面比较广泛.
缺点是, 如果一张小图, 需要 N 个颜色, 就必须做 N 个不同颜色的小图, 合并到大图里面.
使用 data:URL 展示图片, 它可以在页面中渲染图片但无需额外的 HTTP 请求, 请求格式:
<img scr="data:image/jpg;base64, xxxxxxxxxxxxxxxx">
缺点是: 此方案不适合 mobile 应用; IE7 以下不支持; 如果一张图片在多个页面被用到, 无法利用浏览器缓存.
为了解决无法缓存问题, 可以将 data:image 应用到 CSS 样式中, 比如:
- .imageA {
- background-image: url(data:image/jpg;base64, xxxxxxxxxxxxxxxx);
- }
合并脚本和样式表
Multipart XHR
运行客户端用一个 HTTP 请求就可以从服务端传递多个资源. 它通过在服务端将资源 (CSS 文件, html 片段, JavaScript 代码或者 base64 编码的图片) 打包成一个由双方约定的字符串分割的长字符串, 并发送到客户端.
然后用 JavaScript 代码处理这个长字符串, 并根据他的 mime-type 类型和传入的其他'头信息'解析出每个资源.
例如, 解析一串图片编码, 输入为 req.responseText
- function splitImages(imageString){
- var imageData = imageString.split('\u0001');
- var imageElement;
- for (var i =0, len = imageData.length; i<len; i++){
- imageElement = document.createElement('img');
- imageElement.src = 'data:image/jpeg;base64,' + imageData[i];
- document.getElementById('container').appendChild(imageElement);
- }
- }
2. 使用 CDN
内容发布网络 (CDN) 是一组分布在多个不同地理位置的 web 服务器, 用于更加有效地向用户发布内容.
CDN 用于发布静态内容, 如图片, 脚本, 样式表和 Flash.
不使用 CDN 时:
用户在浏览器访问栏中输入要访问的域名.
浏览器向 DNS 服务器请求对该域名的解析.
DNS 服务器返回该域名的 IP 地址给浏览器.
浏览器使用该 IP 地址向服务器请求内容.
服务器将用户请求的内容返回给浏览器.
尽量将 CDN 的域名设置的不同于请求方网站的域名. 比如, 网站为 a.com,CDN 域名可以设置为 acdn.com. 为什么呢?
Cookie 隔离: Cookie 是紧跟域名的, 同一个域名下的所有请求, 都会携带 Cookie. 试想, 海量请求图片或 JS/CSS 文件时, 还要携带 Cookie, 也会成为不小的开销.
并且, 浏览器在同一个时刻向同一个域名请求文件的并行下载数量是有限的(Chrome 为 6 个并发), 所以, 可以利用多个域名主机存放不同的静态资源, 增大页面加载时资源并行下载数量.
3. 利用 HTTP 缓存
具体内容参考文章前端网络高级篇 (三) 浏览器缓存
4. 压缩组件
开启 HTTP Gzip 压缩.
- request: Accept-Encoding: gzip, deflate
- response:Content-Encoding:gzip
5. 将样式表放在顶部
外部脚本文件和 CSS 文件是并行下载的, 把样式表在页面中的位置并不影响下载时间, 但会影响页面的呈现! 浏览器必须要等样式表加载完毕之后才渲染页面.
因此, 应该把样式表放在 head 中, 这样它就能被最先下载使页面逐步呈现.
6. 将 JS 脚本放在底部
一般, JS 脚本是被禁止并行下载的, 因为 JS 脚本可能使用 document.write 来修改页面内容, 所以必须保证 JS 执行顺序.
脚本下载后, 必须执行完, 才可以继续后面的解析.
但是, Chrome 浏览器支持并发下载资源文件, 并保证按顺序执行(参考《WebKit 技术内幕 - 朱永盛》).
7. 避免 CSS 表达式
CSS 表达式是动态设置 CSS 属性的一种强大 (并且危险) 的方式. CSS 表达式求值频率比人们期望的要高, 它们不只在页面呈现和大小变化时求知, 甚至用户鼠标在页面上拖拽都要求知.
如, 将背景色设置为每小时变化一次:
background-color:expression((new Date()).getHours()%2?"#ccc":"#000");
触发频率太高! 不建议使用.
8. 使用外部 JS 和 CSS
纯粹来讲, 内联的 JS 和 CSS 可以产生比外部文件文件更快的响应速度.
但是现实中, 外部链接的 JS 和 CSS 文件会产生较快的页面, 是因为 JS 和 CSS 文件有可能被缓存.
9. 减少 DNS 查找
DNS 也是开销. 通常浏览器查找一个给定主机名的 IP 地址要花费 20~120 毫秒. 在 DNS 查找完成之前, 浏览器不能从主机名哪里下载任何东西.
只要 cline-server 之间保持 TCP 连接打开状态, 就无需 DNS 查找. 所以, 我们可以通过使用 Keep-Alive 和较少的域名来减少 DNS 查找.
Keep-Alive,HTTP1.1 协议中推出的持久连接. 特点为: 只要任意一端没有明确提出断开连接, 则保持 TCP 连接状态.
含有 Keep-Alive 首部的 response 示例:
- HTTP/1.1 200 OK
- Connection: Keep-Alive
- Content-Encoding: gzip
- Content-Type: text/HTML; charset=utf-8
- Date: Thu, 11 Aug 2016 15:23:13 GMT
- Keep-Alive: timeout=5, max=1000
- Last-Modified: Mon, 25 Jul 2016 04:32:39 GMT
- Server: Apache
10. 压缩 JavaScript 和 CSS
可以用各类构建或者编译工具压缩脚本和样式文件, 比如: gulp,webpack
11. 少用 iframe
iframe 是开销最高的 DOM 元素, 它的缺点远大于优点.
不利于 SEO: 搜索引擎的检索程序无法解读 iframe 中的 src
阻塞 onload 事件: iframe 不加载完毕, 就不会触发父窗口的 onload 事件.
影响页面资源并行加载: iframe 和主页面共享连接池, 而浏览器对相同域的连接有限制, 所以会影响页面资源的并行加载.
为了解决两个问题, 可以动态设置 iframe 中的 src 属性, 代码如下:
- <iframe id="iframe1" src="">
- </iframe>
- <script>
- document.getElementById('iframe1').src = "www.api.a.com";
- </script>
12. 少用 Table
table 内容渲染是将 table 的 DOM 渲染树全部生成完并一次绘制到页面上, 所以, 在渲染长表格时很耗性能, 应该尽量避免使用.
可以使用 ul 或 div 替代.
13. JS 文件异步 / 按需加载
有多种方式支持 JavaScript 异步加载.
Script DOM Element
这恐怕是最常见的异步加载脚本方法, 即, 动态创建一个 script 标签, 并设置其 src 值. 如下:
- function createScript(url){
- var scrElem = document.createElement('script');
- srcElem.src = url;
- document.getElementsByTagName('head')[0].appendChild(scrElem);
- }
优点: 支持跨域加载脚本文件; 兼容性最好, 普适性最高的方案
缺点: 脚本无序执行; 会阻塞 onload 事件
XMLHttpRequest
通过 XMLHttpRequest 的方式下载脚本文件, 然后使用 eval 或者动态添加 < script > 标签并设置其 text 属性来执行脚本.
- // 不考虑 IE
- var xhrObj = new XMLHttpRequest();
- xhrObj .onreadystatechange = function(){
- if (xhrObj .readyState == 4) {
- // 方式一
- eval(xhrObj.responseText);
- // 方式二
- var scrElem = document.createElement('script');
- srcElem.text= xhrObj.responseText;
- document.getElementsByTagName('head')[0].appendChild(scrElem);
- }
- }
- xhrObj .open('GET', 'a.js', true);
- xhrObj .send('');
优点: 将脚本下载和脚本执行分离开, 可以在适当的时候再执行脚本; 不会阻塞 onload 事件
缺点; 通过 XMLHttpRequest 获取的脚本文件必须和主页面是同一个域名下. 也就是说, 不支持跨域下载脚本(除非做跨域处理). 因此不适合加载第三方文件; 脚本无序执行.
defer 和 async
两者都支持异步加载文件, 不同之处是, defer 会在全部资源下载完毕后才执行 JS 文件; async 在脚本文件下载完就立刻执行, 并且, async 模式加载的 JS 文件无法依序执行, 对于有顺序依赖的脚本来说, 不应该采用这种方式.
defer 相对友好一些, 并可以保证 JS 文件按照顺序执行.
- <script src="a.js" defer>
- </script>
- <script src="a.js" async>
- </script>
defer 和 async 优点: 支持跨域加载脚本文件.
defer 优点: 可以保证 JS 文件按照顺序执行.
defer 和 async 缺点: IE10 以上 (包括 IE10) 才支持.
async 缺点: JS 文件无法依序执行; 会阻塞 onload 事件
14. 图片懒加载
通过图片懒加载可以让一些不可视的图片不去加载, 避免一次性加载过多的图片导致请求阻塞(浏览器一般对同一域名下的并发请求的连接数有限制), 这样就可以提高网站的加载速度, 提高用户体验.
第一步: 懒加载的 img 标签的 src 设置缩略图或者不设置 src, 然后自定义一个属性, 值为真正的图片或者原图的地址(比如 data-src).
- // https://a.com/logo.png 是图片的真实地址, 设置到 data-src 属性上.
- <img data-src="https://a.com/logo.png" class="lazy-image"/>
- // CSS 部分
- .lazy-image {
- background: url('loading.gif') no-repeat center;
- }
第二步: 页面加载完后, 获取所有需要懒加载的图片的元素集合, 判断是否在可视区域, 如果是在可视区域的话, 设置元素的 src 属性值为真正图片的地址.
- // 监听滚动事件
- document.addEventListener('scroll', inViewShow);
- // 显示图片
- inViewShow() {
- let imageElements = Array.prototype.slice.call(document.querySelectorAll('.lazy-image'))
- let len = imageElements.length
- for(let i = 0; i < len; i++) {
- let imageElement = imageElements[i]
- const rect = imageElement.getBoundingClientRect() // 出现在视野的时候加载图片
- if(rect.top < document.documentElement.clientHeight) {
- imageElement.src = imageElement.dataset.src // 赋值到真正的 src 上
- imageElements.splice(i, 1)
- len--
- i--
- }
- }
- }
也可以使用三方库处理图片懒加载.
15. 避免页面中空的 href 和 src
当 link 标签中的 href, 或者 ifram,script,img 标签的 src 属性为空时, 浏览器在渲染过程中仍然会将 href 和 src 中的空内容进行加载, 直到失败为止. 这样会阻塞页面中其他资源的下载过程.
16. 减少页面重定向
页面重定向会延长页面内容返回的等待时间, 一次重定向大致需要 600 毫秒.
来源: http://www.jianshu.com/p/f08f316ec141