图片太多或者太大将导致页面加载完成缓慢:图片太多导致向服务器请求的次数太多,图片太大导致每次请求的时间过长。
以下是几种优化方案:
1、将图片服务与应用服务分离:
对于服务器来说,图片始终是最消耗系统资源的,如果将图片服务和应用服务放在同一服务器的话,应用服务器很容易会因为图片的高I/O负载而崩溃,所以当网站存在大量的图片读写操作时,建议使用图片服务器.
(注:图片服务器是专门为图片读写操作优化的独立服务器,运行网站的服务器称为应用服务器)
另外浏览器在同一时间对同一域名下的资源的并发请求数目是有限制的,一般在2-6之间,超过限制数目的请求就会被阻塞.一些主流浏览器对 HTTP1.1 和 HTTP 1.0 的最大并发连接数目,可以参考如下图:
把图片服务器与应用服务器分开,图片服务器采用独立域名 ,css、js和图片就可以并发请求了。
2、图片压缩
借助第三方软件来进行压缩,比如 https://tinypng.com/,压缩后分辨率不变,肉眼看不失真。
3、图标字体
一些小图标使用图标字体,通过引入font文件后添加 class 的方式引入,参考: https://icomoon.io/app/#/select
4、图片懒加载
图片懒加载,简单来说就是在页面渲染过程中,图片不会一次性全部加载,会在需要的时候加载,比如当滚动条滚动到某一个位置时触发事件加载图片,如下代码:
// html
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" />
// js
function lazyload(els, n) { //监听页面滚动事件
var n = n || 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历
var num = els.length;
var seeHeight = document.documentElement.clientHeight; //可见区域高度
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
for (var i = n; i < num; i++) {
if (els[i].offsetTop < seeHeight + scrollTop) {
if (els[i].getAttribute("src") == "default.jpg") {
els[i].src = els[i].getAttribute("data-src");
}
n = i + 1;
}
}
}
// 简单的节流函数
//fun 要执行的函数
//delay 延迟
//time 在time时间内必须执行一次
function throttle(fun, delay, time) {
var timeout,
startTime = new Date();
return function() {
var context = this,
args = arguments,
curTime = new Date();
clearTimeout(timeout);
// 如果达到了规定的触发时间间隔,触发 handler
if (curTime - startTime >= time) {
fun.apply(context, args);
startTime = curTime;
// 没达到触发间隔,重新设定定时器
} else {
timeout = setTimeout(fun, delay);
}
};
};
// 采用节流函数
var els = document.getElementsByTagName("img");
window.addEventListener('scroll',throttle(lazyload(els,0),500,1000));
通过js将img标签的data-src属性赋值给src属性
5、精灵图 css Sprites
当网站或者APP有大量小icon,如果上传到图片服务器比如CDN,要加载所有这些小icon将增加大量请求,而CDN是按流量计费的,这无疑将增加很多成本。
CSS Sprites 将小icon合并成一张图片,只需要加载一次,每次通过background-position来控制显示icon,这样就可以节约大量请求。如图:
缺点:在长期开发多人合作的项目中,会不好维护这些sprites,每次对icon做修改,都得相应的改动css里background-position的值,比较繁琐.
6、将图片压缩成base64格式来节约请求
将图片转换成base64,实际上是变大了,并且浏览器在decode base64编码的图片时需要耗费很多时间的,所以如果我们选择此种方案的话,最好选择一些小图片,不然得不偿失,在webpack中可以设置最大多少byte的图片压缩成base64,配置示例如下:
module: {
// 编译器配置
loaders: [
// .css文件用style-loader和css-loader处理
{
test: /\.css$/,
loader: 'style-loader!css-loader'
},
// .styl文件用style-loader、css-loader和stylus-loader编译
{
test: /\.styl(us)?$/,
loader: 'style!css!stylus?sourceMap'
},
// .js文件用jsx-loader编译
{
test: /\.js$/,
loader: 'js-loader?harmony'
},
// 图片用用url-loader处理,小于2k的直接转为base64
{
test: /\.(png|jpg|jpeg)$/,
loader: 'url-loader?limit=2048'
}
]
}
针对decode base64编码的图片比较慢的问题,我们可以选择使用canvas来加速.当向canvas发出绘画命令时,浏览器直接将指令发到GPU实时绘画和渲染图形.因此,我们可以使用canvas来渲染base64编码后的图片,示例代码如下:(TODO:图片更新时优化)
// 缓存图片,并将图片渲染
var renderCvs = function(parent,max){
//将img标签的class设置为lazyload,其父节点是a标签
var lazyloadImage = $('.lazyload',parent);
if(lazyloadImage.length<1){
return;
}
var max = max||lazyloadImage.length;
for(var i=0;i<max;i++){
var imgId = lazyloadImage[i].id;
//再次打开网页,读取缓存,如果有,就从缓存中展示
var imageCache = localStorage.getItem(imgId);
if(imageCache){
lazyloadImage[i].src = imageCache;
continue;
}
var img = new Image();
img.index = i;
img.id = imgId;
img.crossorigin="anonymous";
img.onload = function(){
var _this = this;
var zCvs = $('#'+this.id);
var domCvs = zCvs[0];
domCvs.src = this.src;
zCvs.removeClass('lazyload');
try{
var cvs = document.createElement('canvas');
cvs.style.display = 'none';
document.body.appendChild(cvs);
var rcvs = cvs.getContext('2d');
cvs.width = 140;
cvs.height = 108;
rcvs.drawImage(this,0,0,140,108);
//这里进行缓存 ,toDataURL()方法将url转成base64字符串
setTimeout(function(){
var data = cvs.toDataURL();
//将字符串保存到localStorage中
localStorage.setItem(_this.id,data);
document.body.removeChild(cvs);
},200);
}catch(ex){
}
}
img.src = lazyloadImage[i].getAttribute('data-src');
}
}
7、采用更高压缩比的图片格式
webp、bpg、sharpP等新图片格式具有更好的压缩比,可以使用这类新型的图片来代替原始图片
原理:对图片格式转换,在画质可以接受的情况下达到更好的压缩比效果
优势:减少图片加载流量,效果比较明显
劣势:服务器和浏览器压力增大,而且服务器需要额外的服务支持,格式转换要考虑浏览器的兼容性
8、渐进式加载
先加载低分辨率的图片,再加载高分辨率图片。