JavaScript手写专题——图片懒加载、滚动节流、防抖手写

图片懒加载场景:在一些图片量比较大的网站(比如电商网站首页,或者团购网站、小游戏首页等),如果我们尝试在用户打开页面的时候,就把所有的图片资源加载完毕,那么很可能会造成白屏、卡顿等现象,因为图片真的太多了,一口气处理这么多任务,浏览器做不到啊!我们再想,用户真的需要这么多图片吗?不对,用户点开页面的瞬间,呈现给他的只有屏幕的一部分(我们称之为首屏)。只要我们可以在页面打开的时候把首屏的图片资源加载出来,用户就会认为页面是没问题的。至于下面的图片,我们完全可以等用户下拉的瞬间再即时去请求、即时呈现给他。这样一来,性能的压力小了,用户的体验却没有变差——这个延迟加载的过程,就是 Lazy-Load

搭建图片懒加载场景

通常我们访问网页的时候会出现页面的场景,没来得及被图片填充完全的网页,是用大大小小的空 div 元素来占位的。一旦我们通过滚动使得这个 div 出现在了可见范围内,那么 div 元素的内容就会发生变化

可以设置这样一个html页面

使用data-语法给img标签添加自定义属性,比如使用data-src给img预制一个属性,存储当前图片将要显示的图片路径。之后当元素在可视窗口时通过js将data-src替换给src属性。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Lazy-Load</title><style>.container {display: flex;flex-wrap: wrap;}.img {width: 400px;height: 400px;margin: 10px;background: gray;}</style></head><body><div class="container"><div class="img"><img alt="加载中1" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中2" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中3" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中4" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中5" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中6" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中7" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中8" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中9" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中10" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中11" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中12" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中13" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中14" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中15" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中16" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中17" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中18" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中19" class="pic" data-src="./images/image.avif" /></div><div class="img"><img alt="加载中20" class="pic" data-src="./images/image.avif" /></div></div></body>
</html>

懒加载计算滚动到可视窗口

在懒加载的实现中,有两个关键的数值:一个是当前可视区域的高度,另一个是元素距离可视区域顶部的高度

当前可视区域的高度, 在和现代浏览器及 IE9 以上的浏览器中,可以用 window.innerHeight 属性获取。在低版本 IE 的标准模式中,可以用 document.documentElement.clientHeight 获取,这里我们兼容两种情况:

const viewHeight = window.innerHeight || document.documentElement.clientHeight 

元素距离可视区域顶部的高度,我们这里选用 getBoundingClientRect() 方法来获取返回元素的大小及其相对于视口的位置。对此 MDN 给出了非常清晰的解释:

该方法的返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合, 即:是与该元素相关的 CSS 边框集合 。

DOMRect 对象包含了一组用于描述边框的只读属性——lefttoprightbottom,单位为像素。除了 widthheight 外的属性都是相对于视口的左上角位置而言的。

 lazyload方法

通过图片距离顶部的高度与内容区域的高度进行比较。如果图片没到可视区域,那么imgs[i].getBoundingClientRect().top将大于内容区域高度viewHight。如果图片在可视范围,那么viewHeight-imgs[i].getBoundingClientRect().top的差值将大于0

<script>// 获取所有的图片标签const imgs = document.getElementsByTagName('img')// 获取可视区域的高度const viewHeight = window.innerHeight || document.documentElement.clientHeight// num用于统计当前显示到了哪一张图片,避免每次都从第一张图片开始检查是否露出let num = 0function lazyload(){for(let i=num; i<imgs.length; i++) {// 用可视区域高度减去元素顶部距离可视区域顶部的高度let distance = viewHeight - imgs[i].getBoundingClientRect().top// 如果可视区域高度大于等于元素顶部距离可视区域顶部的高度,说明元素露出if(distance >= 0 ){// 给元素写入真实的src,展示图片imgs[i].src = imgs[i].getAttribute('data-src')// 前i张图片已经加载完毕,下次从第i+1张开始检查是否露出num = i + 1}}}// 监听Scroll事件window.addEventListener('scroll', lazyload, false);
</script>

加载效果图

可以看右侧img的src属性,一开始是没有的,只要alt图片占位。当鼠标滚动到可视区域时src的属性才被替换为真实图片地址。

 

需要注意的是,这个 scroll 事件,是一个危险的事件——它太容易被触发了。试想,用户在访问网页的时候,是不是可以无限次地去触发滚动?尤其是一个页面死活加载不出来的时候,疯狂调戏鼠标滚轮(或者浏览器滚动条)的用户可不在少数啊!

再回头看看我们上面写的代码。按照我们的逻辑,用户的每一次滚动都将触发我们的监听函数。函数执行是吃性能的,频繁地响应某个事件将造成大量不必要的页面计算。因此,我们需要针对那些有可能被频繁触发的事件作进一步地优化。这里就引出了两位主角——throttledebounce

 节流throttle优化懒加载

频繁触发回调导致的大量计算会引发页面的抖动甚至卡顿。为了规避这种情况,我们需要一些手段来控制事件被触发的频率。就是在这样的背景下,throttle(事件节流)和 debounce(事件防抖)出现了。

throttle 的中心思想在于:在某段时间内,不管你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。 

如果在delay秒后立即执行,可以使用时间戳判断。通过new Date方法获取当前时间戳,与上次执行的时间戳进行比较。如果差值超过了delay时间,那么执行fn。这里fn和last都是闭包的变量,throttle执行后,内部的function中仍然能够访问。

  function throttle(fn, delay) {let last = 0;return function () {let args = arguments;let now = +new Date();if (now - last >= delay) {fn.apply(this, args);last = now;}};}

 节流还可以通过定时器异步处理。这个方法要比上面慢,因为setTimeout是异步函数,不会在delay后立即执行,而是等待事件循环处理后执行。

  function throttle2(fn, delay) {let timer = null;return function () {let context = this;//记住thislet args = arguments;//参数if (!timer) {timer = setTimeout(() => {fn.apply(context, args);//执行fntimer = null;}, delay);}};}

给鼠标滚动事件添加节流函数

  const throttleScroll = throttle(lazyLoad, 3000);window.addEventListener("scroll", throttleScroll);

节流效果

可以看到下方,虽然鼠标在滚动,但是页面还是延迟加载

实现防抖debounce

防抖的中心思想在于:我会等你到底。在某段时间内,不管你触发了多少次回调,我都只认最后一次。

 function debounce(fn, delay) {let timer = null;return function () {let context = this;let args = arguments;clearTimeout(timer); //每次都清空定时器timer = setTimeout(() => {//定时器执行fnfn.apply(context, args);}, delay);};}

 鼠标滚动添加防抖效果

  const debounceScroll = debounce(lazyLoad, 1000);window.addEventListener("scroll", debounceScroll);

防抖效果

鼠标疯狂滚动,当鼠标停下来的时候延迟delay加载图片

但是debouce有个问题,debounce 的问题在于它“太有耐心了”。试想,如果用户的操作十分频繁——他每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。

为了避免弄巧成拙,我们需要借力 throttle 的思想,打造一个“有底线”的 debounce——等你可以,但我有我的原则:delay 时间内,我可以为你重新生成定时器;但只要delay的时间到了,我必须要给用户一个响应。这个 throttledebounce “合体”思路,已经被很多成熟的前端库应用到了它们的加强版 throttle 函数的实现中

有底线的防抖——防抖和节流结合体

delay 时间内,我可以为你重新生成定时器;但只要delay的时间到了,我必须要给用户一个响应

function debounce2(fn, delay) {let last = 0;let timer = null;return function (...args) {const context = this;const now = +new Date();if (now - last < delay) {//防抖clearTimeout(timer);timer = setTimeout(() => {fn.apply(context, args);last = now;}, delay);} else {fn.apply(context, args);last = now;}};}

 效果如下,即使鼠标滚动没有停止,到了指定时间一定会执行

throttledebounce 不仅是我们日常开发中的常用优质代码片段,更是前端面试中不可不知的高频考点。“看懂了代码”、“理解了过程”在本节都是不够的,重要的是把它写到自己的项目里去,亲自体验一把节流和防抖带来的性能提升。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/9309.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ICode国际青少年编程竞赛- Python-2级训练场-range函数

ICode国际青少年编程竞赛- Python-2级训练场-range函数 1、 for i in range(4):Dev.turnLeft()Dev.step(i 1)2、 for i in range(4):Spaceship.step(i 1)Spaceship.turnRight()3、 for i in range(4):Spaceship.step(i 1)Dev.step(2)Dev.step(-2)4、 for i in range(1, 5)…

【深度学习】【Lora训练2】StabelDiffusion,Lora训练过程,秋叶包,Linux,SDXL Lora训练

文章目录 一、如何为图片打标1.1. 打标工具1.1.1. 秋叶中使用的WD1.41.1.2. 使用BLIP21.1.3. 用哪一种 二、 Lora训练数据的要求2.1 图片要求2.2 图片的打标要求 三、 Lora的其他问题qa1qa2qa3qa4qa5 四、 对图片的处理细节4.1. 图片尺寸问题4.2. 图片内容选取问题4.3. 什么是一…

2024年3月份宠物行业线上市场数据分析:市场呈现出精细化、品质化趋势

近些年来&#xff0c;养宠物的家庭越来越多&#xff0c;宠物经济也逐渐衍生开来。宠物经济主要是围绕宠物产生的一整条产业链&#xff0c;包括宠物食品、宠物家居、宠物美容和最近火起来的宠物保险等多个领域。目前随着居民人均收入的提高&#xff0c;宠物市场也得到稳步发展。…

【Qt 学习笔记】Qt常用控件 | 输入类控件 | Slider的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 输入类控件 | Slider的使用及说明 文章编号&#xff1a;…

Java | Leetcode Java题解之第80题删除有序数组中的重复项II

题目&#xff1a; 题解&#xff1a; class Solution {public int removeDuplicates(int[] nums) {int n nums.length;if (n < 2) {return n;}int slow 2, fast 2;while (fast < n) {if (nums[slow - 2] ! nums[fast]) {nums[slow] nums[fast];slow;}fast;}return sl…

查看pytorch与cuda对应版本

查看cuda版本 打开cmd,输入nvidia-smi&#xff0c;即可可以看到cuda的版本了 2. pytorch与cuda版本对应关系 可以参考这篇文章&#xff0c;写的非常详细&#xff1a;https://blog.csdn.net/FL1768317420/article/details/134769203

5. 分布式链路追踪TracingFilter改造增强设计

前言 在4. 分布式链路追踪客户端工具包Starter设计一文中&#xff0c;我们实现了基础的Starter包&#xff0c;里面提供了我们自己定义的Servlet过滤器和RestTemplate拦截器&#xff0c;其中Servlet过滤器叫做HoneyTracingFilter&#xff0c;仅提供了提取SpanContext&#xff0…

WebRTC 客户端状态机

WebRTC 客户端状态机 WebRTC 客户端状态机客户端状态机客户端加入流程图客户端离开流程图端到端连接的基本流程 WebRTC 客户端状态机 客户端状态机 首先我们来看一下客户端的一个状态机&#xff0c;客户端与服务器直接通过信令的一个交互之后自然而然的形成一个状态机&#x…

读天才与算法:人脑与AI的数学思维笔记23_人工智能讲故事

1. 伟大的自动语法分析器 1.1. 思维呆板机械的阿道夫奈普&#xff08;Adolphe Knipe&#xff09;一直想成为一名作家&#xff0c;可是他写出来的东西既迂腐又无趣 1.2. 后来&#xff0c;灵光乍现&#xff0c;他得到了一个启示&#xff1a;语言遵循语法规则&#xff0c;这规则…

【连连国际注册_登录安全分析报告】

连连国际注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨…

OPC :快速上手

本系列为OPC技术的快速上以及持续研究和技术实战专栏&#xff0c;将不定期更新。 本章节提供OPC系列技术博文的快速导航。 《OPC服务器简介和入门介绍》 《物联网平台如何为OPC服务器创造新生命力》 《OPC服务器开发之WtOPCSvr——开发文档&#xff08;1&#xff09;》 《OPC服…

何为基差?股指期货的升水和贴水又怎么理解?

基差是一个金融术语&#xff0c;它指的是现货价格和期货价格之间的差额。在股指期货市场中&#xff0c;现货就是指实际的股票指数&#xff0c;而期货则是基于这个指数未来某个时间点的价格预期。基差可以是正的或负的&#xff0c;具体取决于期货价格是高于还是低于现货价格。 1…

每日一题7:Pandas-重命名列

一、每日一题 编写一个解决方案&#xff0c;按以下方式重命名列&#xff1a; id 重命名为 student_idfirst 重命名为 first_namelast 重命名为 last_nameage 重命名为 age_in_years 返回结果格式如下示例所示。 解答&#xff1a; import pandas as pddef renameColumns(studen…

《2024年AI安全报告》:AIML工具使用量飙升594.82%

人工智能&#xff08;AI&#xff09;不仅仅是一种开拓性的创新技术&#xff0c;甚至已经成为一种常态&#xff0c;企业正在工程、IT营销、财务、客户服务等领域迅速采用AI和机器学习&#xff08;ML&#xff09;工具。但与此同时&#xff0c;他们必须平衡AI工具带来的诸多风险&a…

ESP32引脚入门指南(三):从理论到实践(Touch Pin)

引言 ESP32作为物联网领域的明星微控制器&#xff0c;不仅以其强大的网络通信能力著称&#xff0c;还内置了丰富的外设资源&#xff0c;其中就包括电容式触摸传感&#xff08;Capacitive Touch&#xff09;功能。本文旨在深入浅出地介绍ESP32的Touch引脚&#xff0c;带你了解其…

15-LINUX--线程的创建与同步

一.线程 1.线程的概念 线程是进程内部的一条执行序列或执行路径&#xff0c;一个进程可以包含多条线程。 2.线程的三种实现方式 ◼ 内核级线程&#xff1a;由内核创建&#xff0c;创建开销大&#xff0c;内核能感知到线程的存在 ◼ 用户级线程&#xff1a;线程的创建有用户空…

刷题第3天(简单题):LeetCode206--反转链表--双指针法

LeetCode206&#xff1a;给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例…

五一超级课堂---Llama3-Tutorial(Llama 3 超级课堂)---第三节llama 3图片理解能力微调(xtuner+llava版)

课程文档&#xff1a; https://github.com/SmartFlowAI/Llama3-Tutorial 课程视频&#xff1a; https://space.bilibili.com/3546636263360696/channel/collectiondetail?sid2892740&spm_id_from333.788.0.0 操作平台&#xff1a; https://studio.intern-ai.org.cn/consol…

自动镭雕机价格是多少?

自动镭雕机是一种高精度、高效率的激光雕刻设备&#xff0c;广泛应用于手机、电脑、玻璃等产品表面的图案雕刻。那么&#xff0c;自动镭雕机多少钱一台呢&#xff1f;本文将为您详细解析各种因素对自动镭雕机价格的影响。 一、影响自动镭雕机价格的因素 1. 品牌和质量 自动镭…

xiuno(修罗)知乎模板二开优化魔板仿网盘资源社–模板加全套插件

使用说明 以服务器为例搭建教程 ①先安装 PHP7.1 版本 再安装数据库 Mysql ②解压文件&#xff1a;xiunobbs_4.0.4&#xff08;解压到根目录&#xff09;.zip ③解压②完成后找到【plugin】文件夹再解压&#xff1a;plugin(解压到 plugin 文件夹).zip 设置伪静态代码在上面&am…