当前位置:网站首页>防抖和节流(实例讲解)
防抖和节流(实例讲解)
2022-08-02 03:34:00 【IICOOM】
防抖和节流到底是什么?
防抖和节流属于性能优化的知识,它可以有效的降低高频事件触发时,你定义的方法的执行次数。
还是没有感觉???那么,来看下面的场景:
- 用户在搜索框输入关键词(只有当他输入完成时我们才去向服务器发送请求,然后给出搜索结果)
- 自动保存用户填写的表单数据
上面的场景都对应着一个高频事件,即input或者textarea的onKeyUp事件,我们一般是在用户触发这个事件后去向服务器发送请求(这样做的好处是不需要用户去点击搜索按钮,有一种实时查询的感觉)。
那么问题来了,当用户输入一个要查询的关键词,可能需要多次按下和抬起键盘的按键,难道每次onKeyUp的时候我们都要去请求服务器吗?显然不够优雅(因为如果有大量用户同时搜索,服务器压力会很大)。而 防抖(debounce) 正是要解决类似这样的问题。
在浏览器中我们经常会遇到类似的事件(如浏览器scroll,resize,mousemove…)接下来,我们使用 自动保存 的场景来说明一下在 JavaScript 中如何实现防抖。
场景描述:用户在textarea中输入文字后,要为他自动保存到服务器(可以理解为保存为草稿)这时我们需要做的是 优化 请求服务器的次数,需要用到防抖函数。
防抖函数
先来看一个常见的错误写法,注意!!!百度中搜出的很多结果都是这个样子,用了之后就会发现,你的函数还是会立刻执行,并不会延时执行。
function debounce(fn, delay) {
let timer = null
return function (args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(fn.call(this, args), delay)
}
}
问题出在 timer = setTimeout(fn.call(this, args), delay) 这一行。
修改成下面的样子,就可以按设定的delay延时执行了:
function debounce(fn, delay) {
let timer = null
return function (args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
fn.call(this, args)
}, delay)
}
}
// 或者
function debounce(fn, delay) {
let timer = null
return function (args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
fn(args)
}, delay)
}
}
不要小看这小小的区别,它可能会浪费你大量的时间,而且让你对防抖产生怀疑…
下面贴一个完整的例子,还有 防抖在线演示地址,方便你更好的理解这个场景。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>防抖和节流</title>
<style> .de_wrapper {
padding: 20px; display: flex; } .col {
width: 40%; } .log {
height: 300px; overflow-y: scroll; background-color: #fff; } </style>
</head>
<body>
<div class="de_wrapper">
<div class="col">
<h3>未使用防抖(每次按键抬起都会触发保存)</h3>
<textarea name="" id="1" cols="30" rows="10" onKeyUp="printLog(event)"></textarea>
<div id="log" class="log"></div>
</div>
<div class="col">
<h3>使用防抖(停止输入2秒后保存)</h3>
<!-- <textarea name="" id="2" cols="30" rows="10"></textarea> -->
<textarea name="" id="2" cols="30" rows="10" onKeyUp="debounceLog(event)"></textarea>
<div id="log1" class="log"></div>
</div>
</div>
<script> let log = null let log1 = null window.onload = function() {
log = document.getElementById('log') log1 = document.getElementById('log1') // 写法1 // document.getElementById('2').addEventListener('keyup', function(e) {
// debounceLog(e) // }) // 写法2 // document.getElementById('2').addEventListener('keyup', debounceLog) // 写法3 // document.getElementById('2').addEventListener('keyup', debounce(printDebounceLog, 2000)) } function printLog(e) {
log.innerText += `keyup 事件触发【请求服务器保存数据...】: ${
e.target.value}\n` } function printDebounceLog(e) {
log1.innerText += `keyup 事件触发【请求服务器保存数据...】: ${
e.target.value}\n` } let debounceLog = debounce(printDebounceLog, 2000) function debounce(fn, delay) {
let timer = null return function (args) {
if (timer) {
clearTimeout(timer) } timer = setTimeout(function() {
fn(args) }, delay) } } </script>
</body>
</html>
节流函数
节流函数(throttle)与防抖函数的区别:函数节流无论事件触发多么频繁,在一定时间内只会执行一次回调;而函数防抖是在高频事件的最后一次触发回调。
节流函数使用场景:一个很形象的例子就是mousedown发射子弹,每秒只能发出一颗子弹,在线演示地址。
function throttle(fn, limit) {
let lastTime
return function(args) {
if (!lastTime) {
fn.apply(this. args)
lastTime = Date.now()
} else {
if ((Date.now() - lastTime) >= limit) {
fn.apply(this. args)
lastTime = Date.now()
}
}
}
}
// 页面结构
<div class="de_wrapper">
<div class="col">
<h3>未使用节流(点击按钮可以疯狂发射子弹)</h3>
<div class="sky"></div>
<button class="fire_btn">发射</button>
</div>
<div class="col">
<h3>使用节流(发射子弹速度会被限制)</h3>
<div class="sky"></div>
<button class="fire_btn">发射</button>
</div>
</div>
<script>
let sky = null
let sky1 = null
let btn = null
let btn1 = null
window.onload = function() {
sky = document.querySelectorAll('.sky')[0]
sky1 = document.querySelectorAll('.sky')[1]
btn = document.querySelectorAll('.fire_btn')[0]
btn1 = document.querySelectorAll('.fire_btn')[1]
btn.addEventListener('click', fire)
btn1.addEventListener('click', throttle(t_fire, 1000))
}
function fire() {
const b = document.createElement('span')
b.classList.add('bullet')
sky.appendChild(b)
setTimeout(() => {
sky.removeChild(b)
}, 1000)
}
function t_fire() {
const b = document.createElement('span')
b.classList.add('bullet')
sky1.appendChild(b)
setTimeout(() => {
sky1.removeChild(b)
}, 1000)
}
function throttle(fn, limit) {
let lastTime
return function(args) {
if (!lastTime) {
fn.apply(this. args)
lastTime = Date.now()
} else {
if ((Date.now() - lastTime) >= limit) {
fn.apply(this. args)
lastTime = Date.now()
}
}
}
}
总结
- 函数防抖:将多次操作合并为一次操作进行,原理是维护一个计时器,后设置的定时器会取代之前的定时器,如果高频事件一直在触发那么回调函数一直不会执行。
- 函数节流:使得一定时间内只触发一次函数。原理是通过判断是否满足限制时间,满足则执行。
边栏推荐
猜你喜欢
随机推荐
Anaconda(Jupyter)里发现不能识别自己的GPU该怎么办?
MIPI解决方案 ICN6202:MIPI DSI转LVDS转换芯片
Mac安装MySQL详细教程
GM7150 CVBS转BT656视频解码芯片详细内容及设计要求
【TCS3200 color sensor and Arduino realize color recognition】
GM8284DD,GM8285C,GM8913,GM8914,GM8905C,GM8906C,国腾振芯LVDS类芯片
龙讯LT6911系列C/UXC/UXB/GXC/GXB芯片功能区别阐述
STM32F4 CAN 配置注意的细节问题
DMA相应外设映射
判断回文
Chrome 里的小恐龙游戏是怎么做出来的?
【plang 1.4.3】定时器的使用
2020 - AAAI - Image Inpainting论文导读《Learning to Incorporate Structure Knowledge for Image Inpainting》
openwrt RK3568_EVB移植
Altium Designer Basics
TC358860XBG BGA65 东芝桥接芯片 HDMI转MIPI
【Connect the heart rate sensor to Arduino to read the heart rate data】
rosdep update failure solution (pro-test effective)
Compatible with C51 and STM32 Keil5 installation method
剑指Offer 33.二叉搜索树的后序遍历序列