目录
一、图片加载的原理
1、加载目标与加载时机
解析dom是时候遇到img的src会请求资源
构建渲染树时,浏览器根据dom节点对css进行匹配,css没有对应的节点:不发送请求,背景仅在应用的元素在页面中存在时,才会产生请求。
执行js的时候,遇到image对象的src会请求资源
每个页面都有Renderer线程负责渲染页面, 而浏览器有io线程, 用来负责请求资源等。 为什么io线程不是放在每个页面里面而是放在浏览器框架呢?因为这样的好处是如果两个页面页面请求了相同资源的话, 如果有缓存的话就能避免重复请求了。
3、加载拦截
kCSP, //csp内容安全策略检查
// 只信任当前域的图片请求
<meta http-equiv='Content-Security-Policy' content='img-src "self";'>
kMixedContent, //mixed content
// 不允许在https页面中嵌入http请求,可自动升级http为https
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
kOrigin, //secure origin
kInspector, //devtools的检查器
kSubresourceFilter,
kOther,
kNone
4、资源加载优先级
very-high、high、medium、 、low、very-low,
其中MainRescource页面、css、字体这三个的优先级是最高的,然后是script,ajax这种, 而图片、音频的默认优先级是比较低的, 最低的事prefetch预加载的资源。
在优先级在Medium以下的为delayable,即可推迟的, 而大于等于medium的为不可delayable的。从刚刚我们总结的表可以看出:css/js是不可推迟的,而图片, preload的js为可推迟加载:
如图:css样式表优先加载,图片资源处于pending等待状态
5、浏览器最大并发请求数
HTTP/1.1
中,单个TCP连接,在同一时间只能处理一个http请求,虽然存在Pipelining技术支持多个请求同时发送,但由于实践中存在很多问题无法解决,所以浏览器默认是关闭,所以可以认为是不支持同时多个请求。
HTTP2
提供了多路传输功能,多个http请求,可以同时在同一个TCP连接中进行传输。
Chrome浏览器最多允许对同一个域名Host建立6个TCP连接,不同的浏览器有所区别。
如果图片都是HTTPS的连接,并且在同一域名下,浏览器会先和服务器协商使用HTTP2
的Multiplexing
功能进行多路传输,不过未必所有的挂在这个域名下的资源都会使用同一个TCP连接。如果用不了HTTPS或者HTTP2(HTTP2是在HTTPS上实现的),那么浏览器会就在同一个host建立多个TCP连接,每一个TCP连接进行顺序请求资源。
同一域名一般为6个左右,最大不超过10个。
假如一个页面有120个静态资源(css、js、img),并且所有资源都在一个域名下,使用的浏览器最大网络并行请求资源数是6,假设理想一些:所有请求时间都是一样的,每个文件加载需要500ms,则所有资源加载完成需要 120/6 * 0.5 = 10s 的时间。
优化方案:
1、控制首屏资源加载数,合并请求等
2、将静态资源分布在不同的服务器中,使用多个域名,加大并发量
6、资源加载的过程
第一次访问有初始化连接和SSL开销,后面没有说明用的是同一个tcp连接,只有stalled的等待时间
**Queued at :**表示该请求加入到请求队列中的时刻,请求队列在打开F12后第一次发送请求的时候创建,直到关闭控制台的时候销毁。
**Started at :**表示请求开始处理的时刻。
**Queueing:**表示请求从加入到请求队列中到请求开始处理经过的时间。
**Stalled:**请求在可以被发送出去之前的等待时间(阻塞时间),一般是等待可复用的TCP连接释放的时间。浏览器对于单个域名只能同时建立4~6个TCP连接(不同浏览器实现有差异)。
**Proxy Negotiation:**浏览器和代理服务器连接的协商时间。
**DNS Lookup:**域名解析花费的时间。
**Initial Connection:**建立TCP连接花费的的时间,包括TCP握手/重试和协商SSL。
**Request sent:**发送请求花费的时间。
**Waiting (TTFB):**从发出请求到接收到响应第一个字节经过的时间,包括网络延迟时间。
**Content Download:**接收响应花费的时间。
Queueing
请求文件顺序的的排序
Stalled
是浏览器得到要发出这个请求的指令到请求可以发出的等待时间,一般是代理协商、以及等待可复用的TCP连接释放的时间,不包括DNS查询、建立TCP连接等时间等
同域名最大TCP连接并发为6左右,可使用多种域名提高并发量
DNS Lookup
时间执行DNS查找。每个新域pagerequires DNS查找一个完整的往返。 DNS查询的时间,当本地DNS缓存没有的时候,这个时间可能是有一段长度的,但是比如你一旦在host中设置了DNS,或者第二次访问,由于浏览器的DNS缓存还在,这个时间就为0了。
DNS预解析
<link rel="preconnect" href="https://xxx.me">
Initial connection
建立TCP连接的时间,就相当于客户端从发请求开始到TCP握手结束这一段,包括DNS查询+Proxy时间+TCP握手时间。
预建立TCP链接:
<link rel="preconnect" href="https://xxxime.com">
SSL
https加密
Request sent
请求第一个字节发出前到最后一个字节发出后的时间,也就是上传时间
WaitingTTFB
请求发出后,到收到响应的第一个字节所花费的时间(Time To First Byte),发送请求完毕到接收请求开始的时间;这个时间段就代表服务器处理和返回数据网络延时时间了。服务器优化的目的就是要让这个时间段尽可能短。
服务器优化
Content Download
收到响应的第一个字节,到接受完最后一个字节的时间,就是下载时间
压缩资源体积,减少下载时间
参考文献:
1、https://segmentfault.com/a/1190000016369295 《理解浏览器允许的并发请求资源数》
2、https://blog.youkuaiyun.com/qq_34178990/article/details/82822662《从chrome源码看浏览器如何加载资源》
二、懒加载
1、什么是懒加载
有时候一个网页会包含很多的图片,例如淘宝京东这些购物网站,商品图片多只之又多,页面图片多,加载的图片就多。服务器压力就会很大。不仅影响渲染速度还会浪费带宽。比如一个1M大小的图片,并发情况下,达到1000并发,即同时有1000个人访问,就会产生1个G的带宽。
懒加载即延迟加载,需要的时候再进行图片的加载,而不是一次性加载全部图片
2、原理
页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片,只有通过javascript设置了图片路径,浏览器才会发送请求。
一张图片就是一个<img>
标签,浏览器是否发起请求图片是根据<img>
的src属性,所以实现懒加载的关键就是,在图片没有进入可视区域时,先不给<img>
的src赋值,这样浏览器就不会发送请求了,等到图片进入可视区域再给src赋值。
data-xxx
中的xxx
可以自定义,这里我们可以使用data-src
来保存图片链接。
3、懒加载的优点
1、减轻服务器压力
2、减少无效的资源的加载并发加载的资源过多会阻塞js 的加载,影响网站的正常使用
如图:不使用懒加载,需要下载5.9M资源,load耗时1.06s
如图:使用懒加载,仅需下载1.2m资源,load耗时225ms
4、使用场景
懒加载适用于图片较多,页面较长的页面场景中。
1、超长列表滚动时
2、图片资源多,且用户无需第一时间看到全部
4、首屏加载完成了再进行加载,监听load事件
5、代码示例
<div class="container">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
</div>
<script>
var imgs = document.querySelectorAll('img');
function lozyLoad(){
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
var winHeight= window.innerHeight;
for(var i=0;i < imgs.length;i++){
if(imgs[i].offsetTop < scrollTop + winHeight ){
imgs[i].src = imgs[i].getAttribute('data-src');
}
}
}
window.onscroll = lozyLoad();
</script>
//vue项目中使用
<script src="https://image-1251917893.file.myqcloud.com/igame/npm/vue-lazyload@1.3.3/vue-lazyload.js"></script>
如图:首页只加载1、2、3的图片
如图:浏览器往下滑动时,才开始加载4、5、6、7图片
三、预加载
1、什么是预加载
预加载即提前加载,当需要使用时直接从缓存中获取即可。
2、原理
提前加载资源,减少等待时间,优化用户体验
3、预加载的优点
提前加载资源,减少等待时间,优化用户体验
4、使用场景
-
图片等静态资源在使用之前就提前请求,资源使用到的时候能从缓存中加载, 提升用户体验
1、hover显示图片
2、tab切换
3、点击按钮显示图片等需要快速显示图片的场景
5、代码示例
方法1:标签法
使用img标签
<img src="haorooms.jpg" style="display: none" />
//ps:对于用display: none隐藏的元素css背景,不会产生HTTP请求
使用link标签:
<link rel="prefetch" href="image.png">
优点:可在不支持js或未开启js支持的浏览器使用
方法2:image对象
// 给img的src赋值,即可触发请求
var img = new Image();
img.src = "https://xxx.png";
//需要预加载的图片路径存放在数组里
var imgList = [
"1.png",
"2.png",
"3.png"
];
//遍历数组的路径,预加载
for (var i = 0; i < imgList.length; i++) {
var img = new Image();
img.src = imgList[i];
}
方法3:请求、使用其他库 Preload.js
xmlHttpRequest 异步请求. 优点: 可以监控Process 比如现在已经加载了多少.便于操控, 缺点有跨域问题.
使用其他库 比如Preload.js , 这个其实原生也是用src 或者 xmlHTTPRequest实现,默认是XMLHTTPRequest,
官网:https://www.createjs.com/preloadjs
var mainfest = [
{ src: "img/loading.gif" },
{ src: "img/background.png" },
{ src: "img/bg_repeat.jpg" },
// 音频
{ src: "./music/loop.mp3",id:'loop' },
// 视频
{ src: "./video/video_01.mp4",id:'myVideo' }
];
var preload = {
// 预加载函数
startPreload: function () {
var preload = new createjs.LoadQueue(true);
//为preloaded添加整个队列变化时展示的进度事件
preload.addEventListener("progress", this.handleFileProgress);
//注意加载音频文件需要调用如下代码行
preload.installPlugin(createjs.SOUND);
//为preloaded添加当队列完成全部加载后触发事件
preload.addEventListener("complete", this.loadComplete);
//设置最大并发连接数 最大值为10
preload.setMaxConnections(1);
preload.loadManifest(mainfest);
},
// 当整个队列变化时展示的进度事件的处理函数
handleFileProgress: function (event) {
$(".percent").text('loading...' + Math.ceil(event.loaded * 100) + "%");
},
// 处理preload添加当队列完成全部加载后触发事件
loadComplete: function () {
shuangjie.$pageLoad.addClass('hide').next().removeClass('hide')
}
}
preload.startPreload();
方法4:css
/* 伪元素背景预加载 */
.animate-pre-load::after {
content: "";
background: url(https://image-1251917893.file.myqcloud.com/imgOptimization/d-1.png) no-repeat 0 0;
background-size: 100% 100%;
}
/* 动画延迟预加载 */
.animate-pre-load {
animation: animatePre 1s forwards;
animation-delay: 2s;
}
@keyframes animatePre {
0% {}
100% {
background: url(https://image-1251917893.file.myqcloud.com/imgOptimization/b-1.png) no-repeat 0 0;
background-size: 100% 100%;
}
}
总结
懒加载与预加载的区别:
这两种方式都是提高网页性能的方式,两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。