图片懒加载原理,如何实现图片懒加载
这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。
为什么要使用图片懒加载
懒加载这个概念在前端经常被提及,所谓懒加载就是在我们不需要或者不做展示的时候就不予以加载。这样做可以很好地节省内存消耗。在当一个页面图片非常多的时候,做图片懒加载就非常地必要,因为过多过大图片在同时加载的时候会消耗很大的内存以及带宽,造成页面的卡顿。所以做图片懒加载也是前端页面优化的一个可行性途径。
图片懒加载的原理是什么
前端提及懒加载就是不需要或者不展示的时候就不予加载,那么懒加载就有一个非常重要的前提:页面还未展示该元素的时候。应用在图片中,要做到懒加载,我们只需要在图片没有展示的时候不去处理图片,当图片开始或者准备展示在页面中的时候给予图片 src属性。这就是实现图片懒加载的原理。
手动实现图片懒加载
1.创建一个应用懒加载的环境
<!DOCTYPE html>
<html>
<body style="padding: 10px">
<article>
<h2 style="text-align: center;width: 100%">滕王阁序</h2>
<div>
豫章故郡,洪都新府。星分翼轸,地接衡庐。襟三江而带五湖,控蛮荆而引瓯越。物华天宝,龙光射牛斗之墟;人杰地灵,徐孺下陈蕃之榻。雄州雾列,俊采星驰。台隍枕夷夏之交,宾主尽东南之美。都督阎公之雅望,棨戟遥临;宇文新州之懿范,襜帷暂驻。十旬休假,胜友如云;千里逢迎,高朋满座。腾蛟起凤,孟学士之词宗;紫电青霜,王将军之武库。家君作宰,路出名区;童子何知,躬逢胜饯。
时维九月,序属三秋。潦水尽而寒潭清,烟光凝而暮山紫。俨骖騑于上路,访风景于崇阿;临帝子之长洲,得天人之旧馆。层峦耸翠,上出重霄;飞阁流丹,下临无地。鹤汀凫渚,穷岛屿之萦回;桂殿兰宫,即冈峦之体势。
披绣闼,俯雕甍,山原旷其盈视,川泽纡其骇瞩。闾阎扑地,钟鸣鼎食之家;舸舰弥津,青雀黄龙之舳。云销雨霁,彩彻区明。落霞与孤鹜齐飞,秋水共长天一色。渔舟唱晚,响穷彭蠡之滨;雁阵惊寒,声断衡阳之浦。
遥襟甫畅,逸兴遄飞。爽籁发而清风生,纤歌凝而白云遏。睢园绿竹,气凌彭泽之樽;邺水朱华,光照临川之笔。四美具,二难并。穷睇眄于中天,极娱游于暇日。天高地迥,觉宇宙之无穷;兴尽悲来,识盈虚之有数。望长安于日下,目吴会于云间。地势极而南溟深,天柱高而北辰远。关山难越,谁悲失路之人?萍水相逢,尽是他乡之客。怀帝阍而不见,奉宣室以何年?
嗟乎!时运不齐,命途多舛。冯唐易老,李广难封。屈贾谊于长沙,非无圣主;窜梁鸿于海曲,岂乏明时?所赖君子见机,达人知命。老当益壮,宁移白首之心?穷且益坚,不坠青云之志。酌贪泉而觉爽,处涸辙以犹欢。北海虽赊,扶摇可接;东隅已逝,桑榆非晚。孟尝高洁,空余报国之情;阮籍猖狂,岂效穷途之哭!
勃,三尺微命,一介书生。无路请缨,等终军之弱冠;有怀投笔,慕宗悫之长风。舍簪笏于百龄,奉晨昏于万里。非谢家之宝树,接孟氏之芳邻。他日趋庭,叨陪鲤对;今兹捧袂,喜托龙门。杨意不逢,抚凌云而自惜;钟期既遇,奏流水以何惭?
呜乎!胜地不常,盛筵难再;兰亭已矣,梓泽丘墟。临别赠言,幸承恩于伟饯;登高作赋,是所望于群公。敢竭鄙怀,恭疏短引;一言均赋,四韵俱成。请洒潘江,各倾陆海云尔:
滕王高阁临江渚,佩玉鸣鸾罢歌舞。
画栋朝飞南浦云,珠帘暮卷西山雨。
闲云潭影日悠悠,物换星移几度秋。
阁中帝子今何在?槛外长江空自流。
</div>
<img id="img" style="width:100%; height: 1600px" src="https://img1.baidu.com/it/u=3151563348,527195432&fm=253&fmt=auto&app=138&f=JPEG?w=392&h=500" alt="">
</article>
</body>
</html>
<script>
</script>
为了节约篇幅,我们在浏览器中采用移动端的展示方式,将 《滕王阁序》的文章写到页面中,在页面的最下面有一张滕王阁的图片。
当前页面中,在 network 面板中可以看出,在页面刚开始加载的时候就已经开始下载图片了。所以我们需要实现的是: 当页面准备展示图片的时候在进行下载图片资源
2.找到图片展示的时机
图片展示的时机来源于监听页面的滚动,找到图片元素的所在高度的位置,去比较页面滑动的位置。所以实现懒加载的主要途径是通过监听页面的滚动,重写 onScroll 方法。
另外我们要找到图片的展示时机,需要知道下面三个值:
-
网页的可见区域高度
document.documentElement.clientHeight -
滚动高度
document.documentElement.scrollTop || document.body.scrollTop; -
图片所在位置高度
document.getElementById('img').offsetTop
在 onScroll 中打印这三个值,仔细分析他们之间的关系。
可以发现,当 img.offsetTop < seeHeight + scrollTop 的时候开始加载图片,所以有我们可以在这个时间点对 img 的 src 属性赋值。
onscroll = function (e) {
const clientHeight = document.documentElement.clientHeight; // 获取 网页可见区域高度
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 获取滚动条滚动距离
const imgOffset = document.getElementById('img').offsetTop // 图片在网页中的高度
const src = 'https://img1.baidu.com/it/u=3151563348,527195432&fm=253&fmt=auto&app=138&f=JPEG?w=392&h=500'
const img = document.getElementById('img')
if (img.offsetTop < clientHeight + scrollTop) {
console.log('开始加载图片')
img.src = src
}
}
如此一来就可以让图片在正确的时间节点进行加载图片了。
3.性能优化
3.1 重复加载
如上图,虽然图片在合适的时间进行了加载,但是当超过了这个时机之后,图像会被一直不停地赋值。原则上这里应该是只需要赋值一次。所以应该在赋值之前做一个 src 的空值判断。
if (!img.getAttribute('src') && img.offsetTop < clientHeight + scrollTop) {
console.log('开始加载图片')
img.src = src
}
这样可以实现限制为图片的单次加载。
3.2 节流
通过打印我们也可以发现,“开始加载图片”在控制台中频繁被输出,原因不仅是被反复赋值,也是因为 onScroll函数被高频触发,所以这里再加上截流操作是比较合适的。
加上节流函数:
const throttle = function (fn,delay){
let flag = true;
return function(){
if(!flag)
return false;
flag = false;
setTimeout(()=>{
fn()
flag = true;
},delay)
}
}
修改后的完整代码:
<!DOCTYPE html>
<html>
<body style="padding: 10px">
<article>
<h2 style="text-align: center;width: 100%">滕王阁序</h2>
<div>
豫章故郡,洪都新府。星分翼轸,地接衡庐。襟三江而带五湖,控蛮荆而引瓯越。物华天宝,龙光射牛斗之墟;人杰地灵,徐孺下陈蕃之榻。雄州雾列,俊采星驰。台隍枕夷夏之交,宾主尽东南之美。都督阎公之雅望,棨戟遥临;宇文新州之懿范,襜帷暂驻。十旬休假,胜友如云;千里逢迎,高朋满座。腾蛟起凤,孟学士之词宗;紫电青霜,王将军之武库。家君作宰,路出名区;童子何知,躬逢胜饯。
时维九月,序属三秋。潦水尽而寒潭清,烟光凝而暮山紫。俨骖騑于上路,访风景于崇阿;临帝子之长洲,得天人之旧馆。层峦耸翠,上出重霄;飞阁流丹,下临无地。鹤汀凫渚,穷岛屿之萦回;桂殿兰宫,即冈峦之体势。
披绣闼,俯雕甍,山原旷其盈视,川泽纡其骇瞩。闾阎扑地,钟鸣鼎食之家;舸舰弥津,青雀黄龙之舳。云销雨霁,彩彻区明。落霞与孤鹜齐飞,秋水共长天一色。渔舟唱晚,响穷彭蠡之滨;雁阵惊寒,声断衡阳之浦。
遥襟甫畅,逸兴遄飞。爽籁发而清风生,纤歌凝而白云遏。睢园绿竹,气凌彭泽之樽;邺水朱华,光照临川之笔。四美具,二难并。穷睇眄于中天,极娱游于暇日。天高地迥,觉宇宙之无穷;兴尽悲来,识盈虚之有数。望长安于日下,目吴会于云间。地势极而南溟深,天柱高而北辰远。关山难越,谁悲失路之人?萍水相逢,尽是他乡之客。怀帝阍而不见,奉宣室以何年?
嗟乎!时运不齐,命途多舛。冯唐易老,李广难封。屈贾谊于长沙,非无圣主;窜梁鸿于海曲,岂乏明时?所赖君子见机,达人知命。老当益壮,宁移白首之心?穷且益坚,不坠青云之志。酌贪泉而觉爽,处涸辙以犹欢。北海虽赊,扶摇可接;东隅已逝,桑榆非晚。孟尝高洁,空余报国之情;阮籍猖狂,岂效穷途之哭!
勃,三尺微命,一介书生。无路请缨,等终军之弱冠;有怀投笔,慕宗悫之长风。舍簪笏于百龄,奉晨昏于万里。非谢家之宝树,接孟氏之芳邻。他日趋庭,叨陪鲤对;今兹捧袂,喜托龙门。杨意不逢,抚凌云而自惜;钟期既遇,奏流水以何惭?
呜乎!胜地不常,盛筵难再;兰亭已矣,梓泽丘墟。临别赠言,幸承恩于伟饯;登高作赋,是所望于群公。敢竭鄙怀,恭疏短引;一言均赋,四韵俱成。请洒潘江,各倾陆海云尔:
滕王高阁临江渚,佩玉鸣鸾罢歌舞。
画栋朝飞南浦云,珠帘暮卷西山雨。
闲云潭影日悠悠,物换星移几度秋。
阁中帝子今何在?槛外长江空自流。
</div>
<img id="img" style="width:100%; height: 1600px" alt="">
</article>
</body>
</html>
<script>
const src = 'https://img1.baidu.com/it/u=3151563348,527195432&fm=253&fmt=auto&app=138&f=JPEG?w=392&h=500'
const img = document.getElementById('img')
onload = function () {
}
const throttle = function (fn,delay){
let flag = true;
return function(){
if(!flag)
return false;
flag = false;
setTimeout(()=>{
fn()
flag = true;
},delay)
}
}
const layzeLoad = function () {
const clientHeight = document.documentElement.clientHeight; // 获取 网页可见区域高度
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 获取滚动条滚动距离
const imgOffset = document.getElementById('img').offsetTop // 图片在网页中的高度
console.log('lazy load', img.getAttribute('src'), img.offsetTop , clientHeight + scrollTop)
if (!img.getAttribute('src') && img.offsetTop < clientHeight + scrollTop) {
console.log('开始加载图片')
img.src = src
}
}
layzeLoad()
onscroll = throttle(layzeLoad, 1000)
</script>
实现一个简单的图片懒加载其实就这么简单。