百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

前端性能优化之图片懒加载「三种原生实现+vue指令」

wxin55 2025-05-14 17:20 3 浏览 0 评论

什么是懒加载?

懒加载也叫做延迟加载、按需加载,指的是在长网页中延迟加载图片数据,是一种较好的网页性能优化的方式。在比较长的网页或应用中,如果图片很多,所有的图片都被加载出来,而用户只能看到可视窗口的那一部分图片数据,这样就浪费了性能。

如果使用图片的懒加载就可以解决以上问题。在滚动屏幕之前,可视化区域之外的图片不会进行加载,在图片进入可视区域后才加载。这样使得网页的加载速度更快,减少了服务器的负载。懒加载适用于图片较多,页面列表较长(长列表)的场景中。

懒加载带来的好处

  • 减轻服务器的压力
    对于一个展示大量图片的网页,使用懒加载可以显著地减少浏览器向服务器发送的请求数量,降低了服务器的压力,同时也减小了浏览器的负担。
  • 提升用户体验
    如果同时加载所有图片,会导致首屏的加载速度较慢,因为浏览器会把不在可视区域内的图片也一并加载。使用懒加载可以保证浏览器首先加载用户可视区域内的资源,提高用户体验。

懒加载的实现原理/如何实现懒加载

懒加载的核心原理主要是两块:

  1. 将图片的 src 属性置空,阻止图片加载:
    本文采用 data-* 自定义数据属性代替 src 来存储图片资源路径。笔者之前学习的时候也接触过将 src 备份后给 src 赋值为空字符串的方式,原理是一致的。
  2. 判断图片是否在可视区域内
    我们可以借助一些 API 来实现这一功能: HTMLElement.offsetTop、Element.scrollTop、Element.clientHeight、Window.innerHeight、IntersectionObserver

懒加载的其他注意事项:

  • 触发方式
    假设我们判断图片是否在可视区域内并进行图片加载操作的函数是 lazyLoad,所谓触发方式,就是什么时候执行 lazyLoad 这个函数。传统的方式是监听 window 对象的 scroll 事件,即页面发生滚动时触发 lazyLoadIntersectionObserver 这个 API 提供的功能则是对目标元素进行监听,但这是一个比较新的 api,要注意你的项目是否有浏览器兼容需求。它的回调函数触发条件也是被监听元素发生了滚动变化,这就为我们引入了下面两个问题。
  • 防抖/节流
    一次懒加载就可以加载一整个可视页面的图片,而滚动一个页面的距离会触发非常多次滚动事件,造成性能的浪费。针对这种场景,如果采用的是 window.onscroll 方式监听并触发懒加载,需要将懒加载函数套上一层防抖或者节流函数以限制其触发次数;如果采用的是 IntersectionObserver,它会逐个检测这些目标元素是否进入视窗,从而触发对应的加载。这就意味着不会一下子触发所有图片的加载,快速滑过时有的对象的回调函数还没来得及触发就已经不在可视区域内了。IntersectionObserver 会自动帮你管理这些,确保在性能和用户体验之间取得平衡。
  • 快速滑动至页面底部
    大部分网上的代码在不使用 IntersectionObserver 时判断图片是否在可视区域内都仅仅考虑了从上向下滚动时,图片超过在页面底部,就触发懒加载了。那么假设这样一种场景:用户进入页面后快速滚动页面到底部,这样就会导致所有的图片都在可视区域出现过,进一步导致所有图片是否在可视区域内的判断条件都成立了,那么所有图片都会被加载,可能很久都不能加载出用户想看的底部图片。所以如果使用传统的计算方式判断图片是否可见时,既要计算是否超过可视区域底部,也要考虑是否已经滑过了到可视区域上边外部去了,同时配合节流限制懒加载函数的触发频率(这样快速滑动后懒加载被触发时,滑过但超出可视区域顶部的图片就不会被加载了)以达到性能优化。

懒加载的原生实现

首先我们编写一个 html 文件,后面用于展示懒加载的效果:

我们先把其中的 data-src 写成正常的 src,可以看到每次页面刷新时所有的图片都会被直接加载好。如果一个页面中有非常多的图片,这样同时加载所有图片对浏览器和服务器都会造成压力。

使用原生 JS 实现图片懒加载,有下面将呈现的三种方式。说是三种方式,其实就是借助了几种不同的 API,来实现判断当前图片是否在可视区域内。事实上这三种方式是随着原生 JS 推出了新的 API 而演变的,越新的 API 实现起来越便捷,而旧的 API 可以兼容老版本的浏览器。

方式一

第一种方式如下图,图片顶部到文档顶部的距离 > 浏览器可视窗口高度 + 滚动条滚过的高度,此时的图片就是不可见的,如果 图片顶部到文档顶部的距离 < 浏览器可视窗口高度 + 滚动条滚过的高度 那么该图片就应该出现在可视区域内了。
但你还记得我们前面提到的注意事项吗?如果用户直接滑到页面底部,那么这个判断条件对所有的图片都为真,还是会造成性能问题。所以我们要再加上一条判断条件 图片的高度 + 图片顶部到文档顶部的距离 > 滚动条滚过的高度,以确保图片确实在可视区域内,而不只是被滑过。

当理解了这一判断条件之后,就只需要通过对应的 API 获取到他们即可:

  • 待加载图片的高度:img.clientHeight
  • 图片顶部到文档顶部的距离:img.offsetTop
  • 浏览器窗口滚动过的距离:document.documentElement.scrollTop document.body.scrollTop
  • 浏览器可视窗口高度:document.documentElement.clientHeightwindow.innerHeight

下面编写 js 代码:

至此,我们就初步完成了一个图片懒加载功能的实现,让我们来看看效果:

还记得我们先前说过的通过监听滚动的方式实现懒加载需要进行防抖节流处理吗?我们可以看一下现在没有进行处理的代码,在 lazyLoad 函数中打一个 console,可以看到我们简单的五张图片来回滑动一下,滚动事件被触发了好几十次,大量的滚动事件对浏览器性能来说是一个很大的负担。

防抖和节流都可以规避频繁触发回调函数,懒加载应该和哪一种方式结合更好呢?笔者更加推荐采用节流的方式,因为防抖需要用户完全停止滚动一定时间才能进行加载,如果用户在查找指定图片而不松开控制滚动条的鼠标,很难保证完全不触发滚动事件,这样就会导致无法触发懒加载函数,图片展示不出来,带来的用户体验并不好。

添加节流函数对我们的懒加载功能进行优化:

踩坑警告:懒加载的场景不要使用时间戳实现的节流,这会导致当你快速滑到一个位置并立刻停止滑动时,无法进行图片的加载。

看一下添加节流函数后的效果:

同样的操作这次仅仅触发了几次懒加载函数,并且我们可以通过调整节流函数的参数,控制懒加载函数触发的频率,以达到平衡性能和用户体验的最佳效果。

方式二


Element.getBoundingClientRect()
方法返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口的位置。 如下图

所以我们只需要判断 元素相对于可视窗口顶部的距离 < 可视窗口高度 来确保滚动条到了图片的位置,同时 元素相对于可视窗口顶部的距离的绝对值 > 元素本身的高度 来保证图片没有滚到可视窗口上方去。将方法一中的判断条件稍作更改,即得到了方法二:

实现的效果、节流等部分与方法一中一致,这里就不再赘述了,主要区别只是使用了不同的 API。

方式三

IntersectionObserver 是专门为检测某个元素是否出现在可视窗口中而推出的 API,我们这里简单介绍一下我们要用到的东西,对这个 API 感兴趣可以点击跳转到 mdn。

  1. 用法:

const observer = new IntersectionObserver(callback[, options]);

IntersectionObserver 构造函数新建一个对象,接收两个参数callbackoptions

  1. 实例方法
  • observe() 将一个元素加入监听目标集合
  • unobserve() 将一个元素移出监听目标集合
  1. callback
    当监听目标发生滚动变化时触发的回调函数。该回调函数接受一个参数 entries, 它其实是 IntersectionObserverEntry 的实例。简单来讲这个 entries 就存储着我们用 observe() 添加给 observer 实例的那些需要被监听的元素与其根元素容器在某一特定过渡时刻的交叉状态(默认为顶级文档的视窗)。而每一个 entry 有一个 target 属性,指向这个被监听的元素。

话不多说,我们直接上代码:

看看效果:

可以看到这一方法省去了繁琐的可视区域判断表达式,完美的实现了懒加载功能。别说用户快速下滑到页面底部,就是直接从底部开始也不会触发上面图片的加载了,非常的 nice。我们这里并没有进行节流处理,来看一下频繁滚动的场景会发生什么:

console 的位置可以在上面代码的备注中看到,一开始的五次打印是将这五张图片加入监听,在我们向下滚动后图片都加载完成被移出了监听,来回滚动总共就触发了四次回调函数,而且整体的效果也比先前的计算判断条件丝滑很多。笔者也尝试了极速下滑至页面底部,中间的图片有时是不会进行加载的,可见该 API 确实对性能和用户体验进行了很好的平衡。
美中不足的是,mdn 中该 API 的许多页面都标注着实验性技术,如果你的项目有兼容性需求,先检查一下是否可用。

vue 实现懒加载指令 v-lazy

自定义指令 类似于我们常常在模板中使用的 v-ifv-show 等指令,可以通过 app.directive 来全局注册。 我们可以在 src 目录下新建一个 directives 文件夹,存放我们自定义的指令。

main.js 里添加注册我们的 v-lazy 指令:

最后在组件中使用该指令:

这种方式是为了方便快捷的使用懒加载,并且可以自己选择对哪些图片进行懒加载。至于具体的实现,原生中的每一种实现方式在这里都是可以用的,只不过多套了一层自定义指令的壳。

注意事项

自定义指令是在 main.js 中引入的,不会被打包工具编译,只有 src 目录下的文件才会被编译。故而在 'lazy.js' 中是不能够使用相对路径的。解决方案:

  • 使用绝对路径
  • 使用 requie 引入路径(Webpack)/ 使用 getImageUrl 引入路径(Vite)

笔者这里使用的是 Vite,我们稍稍修改用以展示的组件。其实对于本地图片场景,绝对路径也挺方便;如果图片都是在线链接那就最好,无需处理。笔者只想到一种情况需要通过打包工具的静态资源处理来加载图片,那就是图片是存在本地的,选择哪张图片又和数据有关。遇到这样的场景我们只需要把 getImageUrl 的参数修改成对应的变量即可。

但这样写其实不太方便,总要定义 getImageUrl 这个函数,要么写到工具方法中,要么每个需要引入静态资源的组件都要写一遍。所以可以考虑使用
vite-plugin-require-transform
这样的插件来解决此问题。

写在最后

写本文的初衷是因为面试中被问到图片懒加载,才发现自己对这块的理解十分混乱,故而梳理一番。如果感觉本文对你有帮助,可以给笔者点赞收藏关注,怎么方便怎么来,对笔者这个新手前端是莫大的鼓励。最后,感谢阅读,如有错漏,烦请指正。


原文链接:
https://juejin.cn/post/7270792006522257445

相关推荐

总结雅虎前端性能优化技巧(16条)

前言在日常开发中,有很多场景需要我们去做好前端优化,为了防止遗忘,加深记忆,今天参阅了一些资料以及自己的一些总结,梳理出来15条优化技巧。1.合并文件css、js合并,减少http请求数,每次http...

前端掉坑血泪史!4 个 React 性能优化绝招让页面秒开

在前端圈子里摸爬滚打这么多年,我发现React开发时踩坑的经历大家都大同小异。页面加载慢、组件频繁重渲染、状态管理混乱……这些痛点,相信不少前端工程师都感同身受。别愁!今天就给大家分享4个超...

Qwik:革新Web开发的新框架

听说关注我的人,都实现了财富自由!你还在等什么?赶紧加入我们,一起走向人生巅峰!Qwik:革新Web开发的新框架Qwik橫空出世:一场颠覆前端格局的革命?是炒作还是未来?前端框架的更新迭代速度,如同...

大模型服务平台百炼使用

提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。一、通过变量的方式使用平台模板一个好的Prompt可以更好的让模型理解我们的需求,产...

Vue应用性能优化实战:8 个提升页面加载速度的关键策略

一、构建优化与代码精简1.1代码分割与异步加载路由级代码分割:使用动态导入语法拆分路由组件组件级懒加载:结合Suspense实现按需加载javascript//vue-router4.x配置...

前端里那些你不知道的事儿之 【window.onload】

作者:京东科技孙凯一、前言相信很多前端开发者在做项目时同时也都做过页面性能优化,这不单是前端的必备职业技能,也是考验一个前端基础是否扎实的考点,而性能指标也通常是每一个开发者的绩效之一。尤其马上接近...

谷歌站长后台的“核心网页指标”不合格先优化哪个最有效?

根据对上千个网站案例的分析,90%的站长在修复时都陷入“盲目优化”误区——要么死磕服务器配置却忽略图片规范,要么过度压缩JS反而引发CLS布局错位。事实上,移动端页面抖动(CLS)才是60%中小网站的...

Vue3 开发效率拉胯?这 10 个技巧让你开发速度翻倍!

写Vue3项目时,是不是经常被数据更新延迟、组件间传值混乱、页面卡顿这些问题搞得焦头烂额?别担心!今天带来10个超实用的Vue3实战技巧,全是从真实项目中总结出来的“血与泪”经验,帮你...

2024年的JavaScript性能优化:仍然重要吗?

#记录我的9月生活#在不断发展的Web开发领域,新的JavaScript框架和库令人眼花缭乱,很容易让人忽视一些基本的东西。但在这股兴奋之中,性能作为一个卓越用户体验的基石,不能被忽略。为什么?因为...

JS 图片简易压缩【实践】

作者:政采云前端团队转发链接:https://juejin.im/post/5ea574cc518825736e57fcca前言说起图片压缩,大家想到的或者平时用到的很多工具都可以实现,例如,客户端类...

Vue3 开发总踩坑?这 10 个技巧让你少走半年弯路!

前端开发的路上,Vue3虽然强大,但坑也不少!性能优化总没效果?复杂组件通信一头雾水?别担心!今天分享10个超实用的Vue3实战技巧,全是一线开发总结的经验,帮你轻松避开开发雷区,效率直接拉...

前端分享-Vue首屏加载优化

首屏加载速度直接影响用户留存率——当加载时间超过3秒,53%的用户会直接离开(网上来的数据)。Vue单页应用尤需重视,因为传统打包方案会将所有资源打包成巨大的vendor.js,导致用户首次访问时像下...

Core Web Vitals 变了,网站性能这件事得重新关注

现在做网站优化,不能只看速度条,不管你是搞外贸独立站,还是给品牌建站,体验页面这件事你迟早得面对。谷歌这两年把网站的“体验感”提得越来越多,尤其是CoreWebVitals(网页核心指标)一出来,...

页面卡顿到崩溃?5 个实战技巧让前端性能飙升 80%!

作为前端工程师,你有没有遇到过这种情况:精心开发的页面,一上线就被用户吐槽卡顿、加载缓慢,甚至频繁崩溃。明明代码逻辑没问题,可性能就是上不去,这到底是哪里出了问题?别着急,今天就来分享5个超级实用...

周末复习前端js基础知识点总结一,记录完之后好复习(大佬勿喷)

一、深浅拷贝知识1、基本数据类型只有赋值没有拷贝2、数组和对象的赋值是浅拷贝3、结构赋值是深拷贝还是浅拷贝?二、实现深拷贝的几种常用方法方法1、通过json方法深拷贝方法2.基本的封装深拷贝的方法采用...

取消回复欢迎 发表评论: