手把手教你实现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...

相关推荐
沙蒿同学4 分钟前
我把架构约定编译成了会变红的测试:Wails v2 + Go + Vue3 桌面脚手架实战
前端·后端·github
cpolar技术支持7 分钟前
本地 Playwright 测试报告怎么远程复盘?Trace Viewer 跑起来后,用 cpolar 分享失败现场
前端·自动化测试·测试工具·cpolar·playwright
wordbaby7 分钟前
企业级后台管理系统路由设计与最佳实践指南
前端
胡志辉的博客16 分钟前
【完全开源】IP 纯净度检测 可一键部署到自己的CF
前端·javascript·chrome·ip·chromium
Hilaku42 分钟前
作为面试官,我最怕遇到什么样的候选人?
前端·javascript·程序员
TiDi1 小时前
Pinia优化重复请求
前端
web3d5201 小时前
01-用 Leafletjs 10 分钟搭一张水利一张图(Vue3 + Vite 实战)
前端·javascript
晚安日记wanna1 小时前
Vue2 的 defineProperty 差在哪四层追问筛掉九成候选人
前端·vue.js·面试
TiDi1 小时前
吸顶导航交互实现
前端
kisshyshy2 小时前
从Props透传到自定义Hook:系统梳理React跨层级通信与逻辑复用
前端·架构·代码规范