首发于 前端技术指北

前端图片懒加载指北:细数各种方案的实现和对比

前言

当我们在导航栏上输入我们性感的 url 并按下回车的时候,浏览器其实做了非常多的工作,撇去复杂细节不讲,直观上就包括了对资源的请求和下载,以及视图的渲染和呈现。假设我们有一个多图文的新闻资讯类的网站,在网络请求的时候,想要一次性获取所有的图片并加载展示出来,那体验肯定是非常糟糕的。为什么?

首先我们来看看一个图片在 HTML 中是如何表现的:

这其中对于图片资源的引用,我们用到了属性 src

src , 实际上是 sourc 的简称,主要用于替换当前内容。当浏览器执行到 src 的时候,就会暂停其他资源的下载和处理,直到当前的资源加载和执行完毕,这也就是为什么我们的 script 标签尽量应该放在底部的原因。

因此,回到我们上面多图文新闻资讯类网站的假设上,解决多图文呈现的性能和体验问题,最常用的方法就是对资源的下载和处理做一些限制,既图片懒加载

什么是图片懒加载?

图片懒加载,顾名思义,就是懒。那么懒到何种程度?简单来说,就是你翻不到的,我都可能懒得去给你请求,更谈不上渲染呈现给你看了。这样子做的好处,一方面我们避免了一时间加载过多图片造成的网络带宽压力,另一方面也让前端界面加载更快,体验更佳。

基本思想

初始化

img 标签的 src 属性上指向默认占位图,data-src 属性则指向真正图片的 url

视图 scroll 或者 resize

将视图窗口内的所有 img 标签的 data-src 赋值给 src,完成当前图片的加载。

结束

所有的图片都加载完毕,移除 scroll / resize 事件,或者移除相关懒加载监控事件。

图片懒加载实现方案

关于图片懒加载的实现方案,我将其分为三类,分别是视口位置计算交集观察以及 HTML 原生支持

其中, 视口位置计算方案有两个,分别是:

  1. 基于 offsetTop + clientHeight + scrollTop 实现懒加载
  2. 基于 getBoundingClientRect 实现懒加载

交集观察法:

  1. 基于 Intersection Observer API 实现懒加载

HTML 原生支持:

  1. 基于 img 标签的 loading 属性实现懒加载

接下来,分别实现以上 4 种方案来实现图片懒加载。

基于 offsetTop + clientHeight + scrollTop 实现懒加载

原理

offsetTop : 只读属性。代表着一个元素相对于其 offsetParent 的元素顶部内边距的距离。

scrollTop:可以获取一个元素内容垂直滚动的像素数。简单说来,一个元素的 scrollTop 可以代表这个元素的内容顶部到视口顶部的距离,即滚动距离。

clientHeight:元素可视区域范围高度。

在每次触发懒加载计算的时候,程序会对所有的图片都进行判断,如果符合公式 img.offsetTop < document.documentElement.scrollTop + document.documentElement.clientHeight, 即元素偏移距离小于文档滚动高度和视图高度的和,说明元素出现在当前视图底部的上方,那我们就执行替换真正图片的程序,否则跳过不作处理。

代码实现

代码解析

对照上面代码来讲:

  1. count , 一个计数器,代表当前更新了 imgs 数组中多少个元素了。在我们的懒加载中,也会当做下一个要更新元素的下标传给 i

javascript for (let i = count; i < imgs.length; i ++){ // ... }

  1. 在遍历元素上除了基于 count 的第一个元素下标的优化,也应该停止视图之下(可见区域后面)的元素的遍历,因为剩下的元素都是在视图之外,我们并不需要更新。

javascript if (img.offsetTop > scrollTop + viewHeight) { break }

  1. 当遍历的元素没有 data-src 属性的时候,有两种情况:
  2. 当前元素已经遍历更新过,并且移除了data-src 属性。
  3. 当前元素并没有 data-src 属性,意味着不参与懒加载。

不过因为我们总是从 count 开始遍历,因此上面两种情况中, 前者就不存在了,我们只考虑后者。这个时候我们应该选择跳过, 并且 count + 1

  1. 除去上面的情况,我们默认剩下的就是正确有效的元素,执行更新算法,并移除元素的 data-src 属性, count + 1

javascript img.removeAttribute('data-src') img.src = dataSrc count ++

为了避免频繁触发 lazyLaod 函数,我们会选择添加节流功能。本章节使用的是 loadash,当然也可以自己实现一个。

效果



基于 getBoundingClientRect 实现懒加载

原理

使用 getBoundingClientRect 的原理和基于 offsetTop + clientHeight + scrollTop 的原理大同小异。主要不同的点在于,我们使用的是 getBoundingClientRect 方法来返回一个元素的位置信息,而并非上面例子中使用的滚动距离、可视区域高度以及偏移距离来进行计算。

getBoundingClientRect:返回一个元素的大小以及相对于视图窗口的位置,位置信息是相对于视图窗口的左上角来计算的。



图片来源: Element.getBoundingClientRect()

代码实现

代码解析

本例基本的逻辑和上一个方案几乎相同,都需要一个计算步骤来判断是否在视图之内。不同的是,本例中直接使用 getBoundingClientRect() 返回的位置信息,逻辑上更加的清晰。

事实上只需要 top 小于 document.documentElement.clientHeight,我们就可以认为元素正在可视区域内,并执行更新逻辑。

除此之外,其他逻辑大抵和上一个方案类似,具体可以参考上一个方案。

效果



基于 Intersection Observer API 实现懒加载

Intersection Observer API

首先引用 MDN 上的一句描述:

Intersection Observer API 提供了一种异步观察目标元素与祖先元素或顶级文档 viewport 的交集中的变化的方法。

一直以来,前端或多或少存在着一些视图窗口界内的检测以及图片懒加载的需求。长期以往,我们会使用前面两种位置计算类型的方式来实现我们想要的效果。

然而,我们的代码一般都会在主线程上运行,计算类型的方案显得不那么可靠,或多或少都会拉低整个网站的性能,造成一些不好的体验。尤其是在日益繁杂的功能面前,涉及到很多的广告、图片,可能每一个都有自己独立的交集检测,持续的滚动可能都会造成不可避免的方法调用,这在性能上多少会有影响,即便是采用各种优化手段达到一个相对较好的结果,代码逻辑也往往变得更为复杂。这个时候, Intersection Observer API 就应运而生了。

Intersection Observer API 会注册一个回调函数,当元素和另外一个元素(或者浏览器窗口)产生交集变化的时候,回调函数就会被执行。那么,回到我们的图片懒加载,我们完全可以使用这种方法来代替位置计算,提升网站的性能和体验。

tipsIntersection Observer API 尚未被浏览器全部支持,使用的时候需要注意浏览器的支持情况。

原理

使用 Intersection Observer API 实现图片懒加载,可以分为几步走:

  1. 实例化一个 IntersectionObserver 对象 observer,注册回调函数用于处理结果集。
  2. for 循环,观察所有的 img元素。
  3. 回调函数中,每次触发回调传入结果集,遍历判断 isIntersecting 属性,为 true 则将 data-src 赋值给 src 进行更新,并且在更新之后,通过 observer.unobserve 注销对该元素的观察。

代码实现

代码解析

这里没有多么复杂的逻辑,主要的逻辑只有三个。

  1. 实例化一个 IntersectionObserver 实例,注册回调函数。
  1. for 循环遍历所有图片,进行观察:
  1. 针对回调的结果集做判断和处理,最后记得要注销改元素的观察:

效果



基于 img 标签的 loading 属性实现懒加载

简述

前面讲的三种方案都是基于 javaScript 来进行元素位置判断,动态替换 src来实现图片懒加载的效果。那么,有没有一种办法可以更加干脆,而不用这么多逻辑判断,并且性能更佳。答案是,有的。它就是 img 标签上原生支持的属性 loading

我们可以很方便的使用 loading="lazy" 的方式来实现一个图片懒加载的功能,并且不依赖我们的 javaScript, 仅仅依靠原生支持以及浏览器的优化,性能上自然不必多说。

然而,非常遗憾的是,在 chrome 上,也在 75 版本之后才有支持,兼容性简直令人望而却步。因此,如果我们想要首先使用原生支持的 lazy-loading, 依然需要配合 javaScript 做一个可用性判断,以决定当前程序要采用何种懒加载方式。

代码实现

这里我们可以采用两种方式,第一种就是丝毫不借助 javaScript,上来就是干,只需要在 img 标签上加上 loading="lazy" 即可。

简单明了,不用做过多的动作。

当然,因为兼容性的问题,我们也可以在 avaScript 中做一层判断,这样,我们可以采用前面几种方案相同 HTML 结构, 依然给 img 标签赋予 srcdata-src 两个资源。

代码解析

这里没什么好说的。实现上十分简单。在搭配 js 的实现中,只需要判断是否支持loading 属性即可,如果支持,则对所有的 img 进行一些操作:

  1. 添加 loading = "lazy"
  2. data-src 赋值给 src

效果



方案降级

针对以上 4 种方案,在性能体验以及兼容性上各有利弊。或许我们惊喜于原生支持的方案,免去了很多繁杂写法和拥有更优的性能体验,但兼容性却不容乐观,相反,以往看似不过太靠谱的方案在兼容性方面却显得不错。如果我们的程序希望优先选择性能最佳的懒加载方式,完全可以通过策略模式,在当前浏览器环境使用中判断属性支持程度以动态切换懒加载策略,在支持度不高的浏览器中也能降级为兼容性方案。

当然,本文不会去深入实现这个东西。因为这不是这篇文章的重点。相关的功能我会在之后找个时间去实现,希望也能总结出一个博文。

总结

本文细数了 4 种前端图片懒加载的实现方案,其中,位置计算类型的共通思想都是计算元素相对于可视区域的位置,只不过基于offsetTop + clientHeight + scrollTop 的方案中,位置的计算借助了元素的偏移距离、文档的滚动距离以及可视区域高度,而基于 getBoundingClientRect 的方案则是通过这个 API来获取相对于视口左上角的位置。两者相比,后者更加清晰、简单。

Intersection Observer API 的方案,避免了前两种方案位置计算的步骤,只在交集检测的结果集进行操作,当交集变化的时候,回调函数将会被调用,通过判断结果集 isIntersecting 属性即可。

最后一个方案,则是基于 img 标签原生支持的功能,通过设置 loading="lazy" 来实现图片懒加载,不需要借助 js 计算,仅仅依靠原生支持以及浏览器优化,性能更佳。但是兼容性方面目前来讲非常糟糕。

参考文献

Intersection Observer

浏览器IMG图片原生懒加载loading=”lazy”实践指南

Element.getBoundingClientRect()

图片懒加载

代码仓库

github.com/chenhaoren/p

在线直通车

本文首发于: 老生常谈之图片懒加载

玻璃钢生产厂家绵阳玻璃钢树池坐凳批发毕节玻璃钢装饰工程多少钱苏州玻璃钢沙发制作贵州玻璃钢机械外壳生产厂家娄底玻璃钢垃圾桶厂家朝阳玻璃钢天花吊顶制作河源玻璃钢树池厂家直销承德玻璃钢树池价格潮州玻璃钢天花吊顶价格辽阳玻璃钢动物雕塑价格濮阳玻璃钢花钵批发白山玻璃钢人物雕塑加工淄博玻璃钢动物雕塑定制邯郸商场美陈批发晋城玻璃钢花池白山不锈钢花盆制作渭南玻璃钢花盆价格日照玻璃钢沙发公司南通玻璃钢种植池厂萍乡玻璃钢餐桌椅多少钱洛阳玻璃钢座椅公司抚州玻璃钢花池批发阜新商场美陈公司铜仁玻璃钢卡通雕塑厂家直销荆州玻璃钢造型绍兴玻璃钢卡通雕塑定制浙江玻璃钢花池哪家好保山玻璃钢茶几制作乌海玻璃钢家具温州玻璃钢茶几厂家香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化