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.rhkb.cn/news/322687.html

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

相关文章

新手向的s2-046漏洞复现

一、前期准备 1.docker容器 作为第一次接触struts2漏洞类型的小白&#xff0c;第一步从搭建环境开始。首先我们需要准备一个服务器或者本地系统&#xff0c;我这里是使用本地的kali&#xff0c;kali里面需要有docker容器&#xff0c;docker容器的安装教程请自行搜索&#xff0c…

【C++】STL — List的接口讲解 +详细模拟实现

前言&#xff1a; 本章我们将学习STL中另一个重要的类模板list… list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是带头双向循环链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xf…

Prompt提示词教程 | 提示工程指南 | 提示词示例 入门篇

在上一节中&#xff0c;我们介绍并给出了如何赋能大语言模型的基本示例。如果还没看而且是刚入门的同学建议看下&#xff0c;有个基本概念。 Prompt提示词教程 | 提示工程指南 | 提示工程简介https://blog.csdn.net/HRG520JN/article/details/138523705在本节中&#xff0c;我…

2024 GESP6级 编程第一题 游戏

题目描述 你有四个正整数 &#xff0c;并准备用它们玩一个简单的小游戏。 在一轮游戏操作中&#xff0c;你可以选择将 减去 &#xff0c;或是将 减去 。游戏将会进行多轮操作&#xff0c;直到当 时游戏结束。 你想知道游戏结束时有多少种不同的游戏操作序列。两种游戏操作…

为软件教学文档增加实践能力

为了更方便软件教学&#xff0c;我们在凌鲨(OpenLinkSaas)上增加了公共资源引用的功能。 目前可以被引用的公共资源: 微应用常用软件公共知识库Docker模板 引用公共资源 引用微应用 目前微应用包含了主流数据库&#xff0c;终端等工具&#xff0c;可以方便的进行各种相关实…

【前端】HTML基础(1)

文章目录 前言一、什么是前端二、HTML基础1、 HTML结构1.1 什么是HTML页面1.2 认识HTML标签1.3 HTML文件基本结构1.3 标签层次结构1.4 创建html文件1.5 快速生成代码框架 三、Emmet快捷键 前言 这篇博客仅仅是对HTML的基本结构进行了一些说明&#xff0c;关于HTML的更多讲解以及…

容联云孔淼:大模型落地与全域营销中台建设

近日&#xff0c;由金科创新社主办的2024区域性商业银行数智化转型研讨会顺利召开&#xff0c; 容联云产业数字云事业群副总经理、诸葛智能创始人孔淼受邀出席&#xff0c;并分享数智化转型实践经验。 他分享了容联云两大核心产品&#xff0c;“大模型应用容犀Copilot”在金融营…

Spring Security初探

url说明方法/login/oauth/authorize无登录态时跳转到/authentication/require&#xff0c;有登录态时跳转到/loginorg.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint#authorize/authentication/require自己写的用于重定向到登录页面的urlcn.merryy…

PowerDesigner16.7常用配置详解(不断更新)

1 快捷切换name和code 右击调整出按钮&#xff0c;点击按钮即可切换 点击即可切换name和code 2 同时显示name和code&#xff0c;并且显示Comment注释 双击任意一张表&#xff0c;点击columns&#xff0c;再筛选&#xff0c;选中comment后确认 补充好comment信息&#xff0c;…

牛客网刷题 | BC80 奇偶统计

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 任意输入一个正整数…

Linux Ubuntu 开机自启动浏览器

终端输入命令&#xff1a;gnome-session-properties 打开启动设置 如果提示&#xff1a;Command ‘gnome-session-properties’ not found, but can be installed with: apt install gnome-startup-applications 则执行&#xff1a;apt install gnome-startup-applications安装…

1688数据分析实操技巧||1688商品数据采集接口 数据分析

今天&#xff0c;聊一聊B2B平台的数据分析&#xff0c;以1688国内站为例。 1688平台数据接口 1688也属于阿里巴巴的体系&#xff0c;跟淘宝天猫运营很像&#xff0c;因此很多淘宝天猫的玩法调整后也适用于1688。数据分析也是如此。 在1688搞数据分析&#xff0c;搞数据化运营可…

逆向中webpack需要补充的模块很多怎么办

如下面这种典型的形式 进入i找到加载器 找到加载器所在函数r,在 return e[a].call(c.exports, c, c.exports, r),打上断点。 在控制台打印e,会发现它总共有的模块&#xff0c;这些模块需要我们在别的webpack中复制&#xff0c;有时很多&#xff0c;很麻烦。 我们可以注入代码在…

【PMP战报】2024.3.10 PMP考试成绩出炉

PMP成绩查询及电子版证书下载 https://mp.weixin.qq.com/s/HgWrZWjJ0cScEYs4w1b4iwPMP项目管理学习专栏https://blog.csdn.net/xmws_it/category_10954848.html?spm1001.2014.3001.5482 2024年3月中国大陆的认证考试已经顺利结束。 从2024年5月7日开始&#xff0c;大约一周内…

单片机的中断

1. 中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置 当中央处理机CPU正在处理某件事的时候外界发生紧急事件请求&#xff0c;要CPU暂停当前的工作&#xff0c;转而去处理这个紧急事件&#xff0c;处理完以后&#xff0c;再回到原来中断的地方&#xff0c;继续原…

serve服务

全局安装 npm install -g serve 进入目录&#xff0c; 执行serve &#xff0c; 就可直接开启服务

LAME及 iOS 编译

文章目录 关于 LAME编译 for iOS 关于 LAME 官网&#xff1a;https://lame.sourceforge.io LAME是根据LGPL许可的高质量MPEG音频层III&#xff08;MP3&#xff09;编码器。 LAME的开发始于1998年年中左右。Mike Cheng 最开始将它作为针对8hz-MP3编码器源的补丁。在其他人提出…

使用Python及R语言绘制简易数据分析报告

Pytohn实现 在python中有很多包可以实现绘制数据分析报告的功能&#xff0c;推荐两个较为方便的包&#xff1a;pandas-profiling 和 sweetviz 。 使用 pandas-profiling 包&#xff08;功能全面&#xff09; 这个包的个别依赖包与机器学习的 sklearn 包的依赖包存在版本冲突&a…

Stable Diffusion Ai绘画模型推荐:二次元Coriander_Mix v1大模型推荐

负tag嵌入式:EasyNegative,badhandv4 此模型经测试是写实偏3D的效果 画质灰暗的话请加&#xff1a;VAE840000 或者负tag&#xff1a;(watermark:2),(blurry:2),fat,paintings,sketches,(worst quality:2),(low quality:2),(normal quality:2),((monochrome)), ((grayscale))…

jsp驾校管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 驾校管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统采用serlvetdaobean mvc 模式&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发…