从资源加载看图片优化

本文深入探讨了图片加载的原理,包括加载目标与时机、加载拦截和资源加载优先级。接着,介绍了懒加载的概念、原理、优点和使用场景,并通过代码示例展示其应用。预加载的定义、工作方式、优势和适用场景也被详细阐述。最后,总结了懒加载和预加载的差异,强调它们在优化网页性能上的不同策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、图片加载的原理

1、加载目标与加载时机

解析dom是时候遇到img的src会请求资源

构建渲染树时,浏览器根据dom节点对css进行匹配,css没有对应的节点:不发送请求,背景仅在应用的元素在页面中存在时,才会产生请求。

执行js的时候,遇到image对象的src会请求资源

image-20211110170255351

每个页面都有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预加载的资源。

image-20211110151425522

在优先级在Medium以下的为delayable,即可推迟的, 而大于等于medium的为不可delayable的。从刚刚我们总结的表可以看出:css/js是不可推迟的,而图片, preload的js为可推迟加载:

如图:css样式表优先加载,图片资源处于pending等待状态

image-20211111143049717

image-20211110161652705

5、浏览器最大并发请求数

HTTP/1.1中,单个TCP连接,在同一时间只能处理一个http请求,虽然存在Pipelining技术支持多个请求同时发送,但由于实践中存在很多问题无法解决,所以浏览器默认是关闭,所以可以认为是不支持同时多个请求。

HTTP2提供了多路传输功能,多个http请求,可以同时在同一个TCP连接中进行传输。

Chrome浏览器最多允许对同一个域名Host建立6个TCP连接,不同的浏览器有所区别。

如果图片都是HTTPS的连接,并且在同一域名下,浏览器会先和服务器协商使用HTTP2Multiplexing功能进行多路传输,不过未必所有的挂在这个域名下的资源都会使用同一个TCP连接。如果用不了HTTPS或者HTTP2(HTTP2是在HTTPS上实现的),那么浏览器会就在同一个host建立多个TCP连接,每一个TCP连接进行顺序请求资源。

同一域名一般为6个左右,最大不超过10个。

假如一个页面有120个静态资源(css、js、img),并且所有资源都在一个域名下,使用的浏览器最大网络并行请求资源数是6,假设理想一些:所有请求时间都是一样的,每个文件加载需要500ms,则所有资源加载完成需要 120/6 * 0.5 = 10s 的时间。

优化方案:

1、控制首屏资源加载数,合并请求等

2、将静态资源分布在不同的服务器中,使用多个域名,加大并发量

6、资源加载的过程

image-20211112174203132

image-20211112115100514

第一次访问有初始化连接和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 的加载,影响网站的正常使用

image-20211108160239293

如图:不使用懒加载,需要下载5.9M资源,load耗时1.06s

image-20211112170920349

如图:使用懒加载,仅需下载1.2m资源,load耗时225ms

image-20211112171020713

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的图片

image-20211111113809423

如图:浏览器往下滑动时,才开始加载4、5、6、7图片

image-20211111113855425

三、预加载

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%;
      }
    }

image-20211111155156576

总结

懒加载与预加载的区别:
这两种方式都是提高网页性能的方式,两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值