防抖和节流是高频操作的优化方案,防抖是把连续的高频操作优化为最后一次操作(常用方案略有不同,此处仅介绍思路),节流是将高频操作降频,固定时间内执行一次。
先通过触发浏览器的mousemove事件来介绍一下防抖和节流的实现:
1 // html
2 <body>
3 <div id="content">0</div>
4 </body>
5
6 // js
7 function count() {
8 document.getElementById('content').innerHTML = Math.random().toFixed(6)
9 }
10
11 window.onmousemove = count
在不使用防抖的情况下直接给window绑定事件,只要鼠标移动,事件会一直被触发。
1.防抖debounce
可以在事件被触发时不直接执行处理函数,而是设定一个计时器,事件(短时间内)再次被触发就重新开始计时,直到超过一定间隔后才执行函数。
1 function debounce(func, wait) { 2 let timeout 3 return function () { 4 if (timeout) { 5 clearTimeout(timeout) 6 } 7 timeout = setTimeout(func, wait) 8 } 9 } 10 window.onmousemove = debounce(count, 1000)
查看完整版:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Debounce</title> 7 <style> 8 *{ 9 margin: 0; 10 padding: 0; 11 border: 0; 12 } 13 #content{ 14 box-sizing: border-box; 15 height: 100vh; 16 100vw; 17 padding-top: 60px; 18 background-color: #87ceeb; 19 font-size: 60px; 20 text-align: center; 21 color: #fff; 22 overflow: hidden; 23 } 24 </style> 25 </head> 26 <body> 27 <div id="content">0</div> 28 </body> 29 <script> 30 function count() { 31 document.getElementById('content').innerHTML = Math.random().toFixed(6) 32 console.info(Math.random().toFixed(6)) 33 } 34 // window.onmousemove = count 35 36 // 防抖 - 超过时间间隔后触发一次处理函数 37 function debounce(func, wait) { 38 let timeout 39 return function () { 40 if (timeout) { 41 clearTimeout(timeout) 42 } 43 timeout = setTimeout(func, wait) 44 } 45 } 46 // window.onmousemove = debounce(count, 1000) 47 48 // 防抖 - 先执行一次处理函数 49 function debounceAdvance(func, wait) { 50 let timeout 51 return function () { 52 let context = this 53 let args = arguments 54 if (timeout) { 55 clearTimeout(timeout) 56 } 57 let callNow = !timeout 58 timeout = setTimeout(() => { 59 timeout = null 60 func.apply(context, args) 61 }, wait) 62 if (callNow) { 63 func.apply(context, args) 64 } 65 } 66 } 67 window.onmousemove = debounceAdvance(count, 500) 68 </script> 69 </html>
2.节流throttle
节流是把高频操作降低到一段时间内(delay)执行一次,可以在第一次出发时创建一个时间戳prev,和再次触发事件的时间戳now比较,如果间隔大于delay就触发事件,重新计时
1 // 时间戳版 2 let throttle = function(func, delay){ 3 let prev = Date.now() 4 return function() { 5 let now = Date.now() 6 if(now - prev >= delay){ 7 func() 8 prev = Date.now() 9 } 10 } 11 } 12 window.onmousemove = throttle(handle, 1000)
或者使用定时器完成,完整版:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Throttle</title> 7 <style> 8 *{ 9 margin: 0; 10 padding: 0; 11 border: 0; 12 } 13 #content{ 14 box-sizing: border-box; 15 height: 100vh; 16 100vw; 17 padding-top: 60px; 18 background-color: #87ceeb; 19 font-size: 60px; 20 text-align: center; 21 color: #fff; 22 overflow: hidden; 23 } 24 </style> 25 </head> 26 <body> 27 <div id="content">0</div> 28 </body> 29 <script> 30 function handle() { 31 document.getElementById('content').innerHTML = Math.random().toFixed(6) 32 console.info(Math.random().toFixed(6)) 33 } 34 // window.onmousemove = handle 35 36 // 时间戳版 37 let throttle = function(func, delay){ 38 let prev = Date.now() 39 return function() { 40 let now = Date.now() 41 if(now - prev >= delay){ 42 func() 43 prev = Date.now() 44 } 45 } 46 } 47 // window.onmousemove = throttle(handle, 1000) 48 49 // 定时器版 50 let throttle2 = function(func, wait){ 51 let timeout 52 return function() { 53 if(!timeout){ 54 timeout = setTimeout(()=>{ 55 func() 56 timeout = null 57 }, wait) 58 } 59 } 60 } 61 62 window.onmousemove = throttle2(handle, 1000) 63 64 </script> 65 </html>
3.应用场景
常用于用户不断改变浏览器大小、鼠标移动和输入验证的一些处理,这些最后触发一次的使用防抖即可;
鼠标不断点击、鼠标滚动不断加载等适合使用节流throttle.
说下我的案例:
一个数据在线编辑平台,用户需要不断输入X、Y、Z坐标调整物体位置,希望不用离开输入框就能实时看到变化,如果监听keyup事件就会过于频繁,这时就适合使用防抖。
另外一个是在地图中加载了20w+模型,每次移动地图都会触发模型的重新渲染,这是就需要再地图的move事件中进行一些干预,可以用防抖,不过幸运的是地图有moveend事件,也算是一种防抖吧。