前端代码交给浏览器后,浏览器会干什么?
第一步:下载html 标签,接着会在内存之中建立DOM树
第二步:下载css样式表,于是便开始渲染树
第三步:DOM树再加上CSS渲染树完成后就得到了页面
接着进行:JavaScript执行、事件处理、资源加载、页面交互等等
而这次主要介绍的是前三步浏览器渲染页面时的加载方式------懒加载
懒加载
懒加载是一种常用的编程技术,其核心思想是推迟对象的创建或数据的加载直到真正需要它们的时候。这种技术可以提高应用的启动速度,减少内存占用,并提高性能。
来看这么一段代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_753139.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_336055.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_753139.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_533951.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_322707.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_430418.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_186979.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_534962.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_345749.jpg">
<img src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_979955.jpg">
</body>
</html>
会不会不经思考,这些图片会在进入页面的同一时间生成,那么如果这个页面带有很多很多张图片或者链接和程序,这个浏览器会不会需要加载很多时间呢?那么用户的体验感肯定非常不好
- JS是单线程的
- 而浏览器是多线程的
- 浏览器还会启动新的下载线程,比如
img、link、script
等等
因此为了避免浏览器加载"堵车",便可以使用到懒加载,滑到哪就开始加载哪里
开始实现懒加载
第一步、避免图片被直接加载
将图片的标签属性改为数据属性data-src
添加滚动事件
鼠标滚动触发loadImage
函数
html
<body>
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_753139.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_336055.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_753139.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_533951.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_322707.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_430418.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_186979.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_534962.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_345749.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_979955.jpg">
<script>
const imgs = document.getElementsByTagName('img')//该函数通过document.getElementsByTagName方法获取了页面中所有<img>标签元素,并将其存储在imgs变量中。
const num = imgs.length
function loadImage(){
console.log('haha');
}
window.addEventListener('scroll',loadImage);//鼠标滚动触发loadImage函数
</script>
</body>
第二步、编写Javascript使图片加载
确定滑到了哪张图片的位置
-
先获取整屏的高度
screenHeight
-
再获取滚动条当前滑动的距离
scrollTop
-
再计算图片距离屏幕顶部的距离
offsetTop
-
如果如果图片距离顶部的距离小于滚动条滚动的距离加上可视区域的高度,则加载出图片
js
<script>
const imgs = document.getElementsByTagName('img')
const num = imgs.length
function loadImage(){
console.log('haha');
// 是否在可视区
let screenHeight = document.documentElement.clientHeight//获取可视区域的高度,为一屏高度
let scrollTop = document.documentElement.scrollTop// 获取滚动条滚动的距离
|| document.body.scrollTop //兼容性写法,不同浏览器的兼容性问题 ||表或者的意思
for(let i = 0; i < num; i++){
if (imgs[i].offsetTop < scrollTop + screenHeight)//如果图片距离顶部的距离小于滚动条滚动的距离加上可视区域的高度,则加载图片
{
//console.log(imgs[i]);
//console.log(imgs[i].dataset.src);//数据属性 dataset.src
//console.log(imgs[i].dataset.src,imgs[i].dataset.price);
imgs[i].src = imgs[i].getAttribute('data-src')//获取data-src属性的值
}
}
}
window.addEventListener('scroll',loadImage);//鼠标滚动触发
</script>
至此浏览器下载html 标签,并且在内存之中建立DOM树
第三步、下载css样式表开始渲染树
这里就简单的设置一下CSS代码
css
*{
margin: 0;
padding: 0;
}
body{
background-color: bisque;
}
img {
display: block;
margin-bottom: 50px;
width: 400px;
height: 400px;
}
这样整体效果就实现了 只不过还有存在一些问题需要解决
- 监听事件太敏感,滑动时事件次数过多会导致存在内存占用过高问题
- 在点击进入页面时,页面中间的图片并没有加载出来,只有在滑动后才生成图片
- 在加载完成所有图片后,监听事件依然存在
第四步、取消事件监听、显示初始页面
在加载完成所有图片后取消事件监听
在for
循环中添加window.removeEventListener
取消事件监听
js
for(let i = 0; i < num; i++){
if (imgs[i].offsetTop < scrollTop + screenHeight)//如果图片距离顶部的距离小于滚动条滚动的距离加上可视区域的高度,则加载图片
{
//console.log(imgs[i]);
//console.log(imgs[i].dataset.src);//数据属性 dataset.src
//console.log(imgs[i].dataset.src,imgs[i].dataset.price);
imgs[i].src = imgs[i].getAttribute('data-src')//获取data-src属性的值
n = i + 1;
if(n == num){
//console.log('加载完毕');
window.removeEventListener('scroll',loadImage);//完成所有图片加载后,取消滚动事件监听
}
}
}
解决在点击进入页面时,页面中间的图片并没有加载出来问题只需要在loadImage()
函数前再声明提升一下loadImage()
第五步、防抖节流
为了解决监听事件太敏感,滑动时事件次数过多会导致存在内存占用过高问题
导入方法库`lodash
html
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
使用了lodash
库(通过_
符号表示)中的throttle
函数来创建一个新的函数throttleLayLoad
。对原始loadImage
函数的一个节流,限制loadImage
函数的执行频率,无论loadImage
函数被调用得多频繁,throttleLayLoad
都会确保它至少每隔200毫秒才执行一次。
更改鼠标滚动触发的函数为新的函数throttleLayLoad
js
const throttleLayLoad = _.throttle(loadImage,200)
window.addEventListener('scroll',throttleLayLoad);
总结
通过图片懒加载技术,我们不仅优化了网页的加载速度,还提升了用户体验,是现代网页开发中不可或缺的性能提升策略。随着技术的演进,懒加载也逐渐集成到各种前端框架和库中,使得实现更为便捷高效。
- 提高性能:通过减少不必要的资源加载,可以提高应用的响应速度和性能。
- 节省资源:减少内存和带宽的使用,特别是在资源受限的环境中。
- 提升用户体验:用户可以更快地看到他们需要的内容,而不是等待所有内容一次性加载完成。
(附上完整代码)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./common.css">
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
</head>
<body>
<img data-price="20" data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_753139.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_336055.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_753139.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629428917_533951.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_322707.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_430418.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_186979.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_534962.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_345749.jpg">
<img data-src="https://img.3dmgame.com/uploads/images/news/20210820/1629429058_979955.jpg">
<script>
const imgs = document.getElementsByTagName('img')
const num = imgs.length
loadImage()
function loadImage(){
console.log('haha');
// 是否在可视区
let screenHeight = document.documentElement.clientHeight//获取可视区域的高度,为一屏高度
let scrollTop = document.documentElement.scrollTop// 获取滚动条滚动的距离
|| document.body.scrollTop //兼容性写法,不同浏览器的兼容性问题 ||表或者的意思
let n = 0
for(let i = 0; i < num; i++){
if (imgs[i].offsetTop < scrollTop + screenHeight)//如果图片距离顶部的距离小于滚动条滚动的距离加上可视区域的高度,则加载图片
{
//console.log(imgs[i]);
//console.log(imgs[i].dataset.src);//数据属性 dataset.src
//console.log(imgs[i].dataset.src,imgs[i].dataset.price);
imgs[i].src = imgs[i].getAttribute('data-src')//获取data-src属性的值
n = i + 1;
if(n == num){
//console.log('加载完毕');
window.removeEventListener('scroll',throttleLayLoad);//完成所有图片加载后,取消滚动事件监听
}
}
}
}
const throttleLayLoad = _.throttle(loadImage,200)//节流函数
window.addEventListener('scroll',throttleLayLoad);//鼠标滚动触发
</script>
</body>
</html>