手把手教你实现js懒加载

js懒加载问题是个非常容易被问到的面试点,如果你还不清楚如何实现,不妨一起学习下

懒加载

所谓懒加载就是需要使用你的时候再加载,通常就是浏览网页的时候,你下拉很多图片的网页,边拉边加载,你可以看得出来下面的图片在展现你面前之前是没有加载出来的

自己实现

我们自行去网络上搜10张图,然后给10张图加个样式,让它们成一个块级元素,从上到下显示

xml 复制代码
<style>
    .img-item {
        display: block;
        height: 300px;
        margin-top: 50px;
    }
</style>
ini 复制代码
<body>
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.5053ecdbef05fa7726aa489d27b52e40&pid=Wdp&w=612&h=304&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.c5db2c88af1a76f18d0efe02fcded91d&pid=Wdp&w=612&h=304&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.c5129de8701c4a933d92cd6bf832b233&pid=Wdp&w=300&h=156&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.afe7f6448d6eba0055cd8ce9ac9fdf62&pid=Wdp&w=300&h=156&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.e168b9c5da30772083104ed0f4ef0ecf&pid=Wdp&w=612&h=304&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.8025ce5a977b3826589022cede69e110&pid=Wdp&w=300&h=156&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.a58ae29e32e20a27d498eed19528ee3c&pid=Wdp&w=300&h=156&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.2049b527600b31b2cd863a380be59848&pid=Wdp&w=300&h=156&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.9f51912b8b6c19a9891b380ad526db85&pid=Wdp&w=612&h=304&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
    <img class='img-item' src="https://th.bing.com/th?id=ORMS.1b6375ea147b5704f9d073a326e1fc2a&pid=Wdp&w=300&h=156&qlt=90&c=1&rs=1&dpr=1.25&p=0" alt="">
</body>

想要让这些图片进入到可视区内加载,进入之前不加载,就需要去动src,src有值了就一定会去加载,所以我们可以先把url放到另一个伪属性中去,等到了图片进入到可视区内再去把伪属性的url赋给src

如何获取元素的几何信息?

top和bottom都是自己到最顶部的距离,均已浏览器顶部为准

querySelector是获取第一个元素,你会发现getBoundingClientRect给你拿到了bottom和top属性

只要每个图片的top值比窗口的高度小,那么就代表着图片进入可视区范围内

ini 复制代码
imgs.forEach(el => {
	let rect = el.getBoundingClientRect()
	if(rect.top < viewHeight) {
		// 赋值逻辑......
	}
})

data-original的值赋给src需要先交给第三方,这个第三方也需要是image标签,正常情况是需要createElement添加一个标签的,但是image标签非常之特殊,它本身就具有构造函数

ini 复制代码
let image = new Image()
image.src = el.dataset.original

data-有个专有写法就是dataset,因此data-original就是dataset.original

接下来,通过load事件来确保image这个标签在创建完成之前是不会执行赋值的

ini 复制代码
image.onload = function() {
    el.src = image.src 
}

这里肯定有小朋友要提出一个问题了,为何不直接写el.src = el.dataset.original。确实,这样写也没有问题,只不过浏览器对图片是有个缓存机制的,靠第三方的image标签纯粹是为了onload,看图片是否被浏览器加载完毕,加载完毕才把值赋过去,如果直接赋值,加载的时候是空白的,加载是需要时间的。Image对象的src属性设置为要加载的url时,浏览器会异步开始加载该图片,这样图片可能已经被缓存了,从而提高了加载速度

图片一旦加载就不需要二次加载了,所以我们把data-original这个属性移除掉

arduino 复制代码
el.removeAttribute('data-original')

因为imgs数组获取的时候就是获取带有data-original属性的图片

接下来就需要让用户滚动页面去触发这个事件,所以来一个监听滚动事件

javascript 复制代码
document.addEventListener('scroll', lazyLoad)

最终代码如下

xml 复制代码
<script>
    let viewHeight = window.innerHeight
    function lazyLoad() {
        let imgs = document.querySelectorAll('img[data-original]') 
        imgs.forEach(el => {
            let rect = el.getBoundingClientRect() 
            if(rect.top < viewHeight) {
                let image = new Image()
                image.src = el.dataset.original
                image.onload = function() {
                    el.src = image.src 
                }
                el.removeAttribute('data-original') 
            }
        })
    }
    lazyLoad() // 页面初始加载时调用一次
    document.addEventListener('scroll', lazyLoad)
</script>

边下划,src边被赋值,最终实现效果如下

最后

因为图片是非常占用浏览器性能的,懒加载的意义就是提高页面加载速度,降低资源浪费,提升用户体验。

懒加载的核心原理其实就是通过一个伪src属性,当图片到达了可视区范围内,就将伪src属性赋值给src

另外有不懂之处欢迎在评论区留言,如果觉得文章对你学习有所帮助,还请"点赞+评论+收藏"一键三连,感谢支持!

本次学习代码已上传至本人GitHub学习仓库:github.com/DolphinFeng...

相关推荐
lichenyang4535 小时前
Docker 学习笔记(一):为什么需要镜像、容器和仓库?
前端
kyriewen6 小时前
别再对着 TypeScript 报错发呆了:我把 10 个最常见的红色波浪线翻译成了人话
前端·javascript·typescript
IT_陈寒6 小时前
SpringBoot自动配置的坑,我的API突然就404了
前端·人工智能·后端
free356 小时前
从 0 实现一个 Tiny JavaScript VM:项目架构拆解
javascript
奇奇怪怪的7 小时前
Embedding 模型 10+ 横向评测
前端
陈广亮7 小时前
Monorepo 从 0 到 1 实操指南 2026 版:pnpm catalogs + Turborepo 2.x + changesets 全链路
前端
子兮曰7 小时前
OpenMontage 深度解剖:你的 AI 编程助手,其实是个视频工作室
前端·后端·ai编程
敲代码的鱼7 小时前
PDF 预览与签名批注写回 支持安卓 iOS 鸿蒙 UTS插件
android·前端·ios
子兮曰7 小时前
前端工具链的「Rust 化」:一场没有赢家的军备竞赛?
前端·后端·rust