前端网络高级篇(六)网站性能优化
网站性能优化可以从下面总结点入手。
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
的方式下载脚本文件,然后使用eva
l或者动态添加<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毫秒。
- 数据库中间件mysql-proxy细节【mysql官方的中间件】
- Office CVE-2017-8570远程代码执行漏洞复现
- Java 并发包中的读写锁及其实现分析
- 深入理解 Spring 事务原理
- Chrome开发者工具的小技巧
- Java Web中JSP中6种动作概况知识点总结——每日一语法学习
- 从Flash到Silverlight进阶教程-用代码来创建动画
- 从Flash到Silverlight进阶教程-Tweener
- silverlight设置浏览器Cookies
- 一个最基本的布局控件-panel
- silverlight项目小结
- oozie 运行demo
- sqoop 兼容性问题
- oozie 安装过程详解
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Asp.Net Mvc表单提交(批量提交)
- Vue 基本指令和html常用标签结合使用综合案例(含代码)
- ef和mysql使用(二)--让mysql支持EntityFramework.Extended实现批量更新和删除
- EF 多种查询方式
- (mysql)找不到请求的 .Net Framework Data Provider。可能没有安装
- Vue 中的 v-cloak 解读
- ef和mysql使用(一)
- 浏览器工作原理
- ef oracle参数化问题
- 动态We API(ABP官方文档翻译)
- 彻底禁用Chrome的“请停用以开发者模式运行的扩展程序”提示
- wcf远程服务器返回错误404
- EF 相见恨晚的Attach方法
- C#篇(三)——函数传参之引用类型和值类型
- linq中order by 和group by (含lambda表达式实现)以及综合案例