小当家ISV
互联网高新技术服务商

全球共有10多个国家和地区1000多个平台,20万+商户使用

前端性能优化 | 防抖与节流

作者:    何坤隆       发布时间:     2023-11-11          1273      分类专栏: 前端开发

一. 前言

为什么要做性能优化?性能优化到底有多重要?  性能优是为了提供更好的用户体验加快网站加载速度提高搜索引擎排名节省服务器资源适应多种设备和网络环境等方面的需求。通过不断优化性能,可以提高用户满意度、增加网站流量提高业务效果。

同时性能优化是把双刃剑,有好的一面也有坏的一面。好的一面就是可以能提升网站性能,坏的一面就是配置多,代码复杂,或者要遵守的规则太多。并且某些性能优化规则并不适用所有场景,所以也并不是一味的追求性能优化,而是需要谨慎使用。

防抖和节流JavaScript 中常用的两种性能优化方式。面试中我们也会经常碰到。它们的作用是减少函数的执行次数,以提高代码的性能。本文将详细介绍防抖节流的定义、原理和实现方法,并讨论如何在具体功能中使用它们。

二. 对防抖与节流的理解

什么是防抖(Debounce)

如下图所示,防抖(Debounce)是指在事件被触发 delay 时间后再执行回调 function 函数,如果在这设置的 delay 时间内事件又被触发,则重新计时。这可以使用在些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

image.png

什么是节流(Throttle)

如下图所示,节流(Throttle)是指规定一个单位时间(延迟 delay 时间),在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

image.png

三. 防抖函数的应用场景

  1. 搜索框实时搜索:当用户在搜索框中输入内容时,通常需要实时进行搜索。使用防抖函数可以延迟搜索请求的发送,只在用户停止输入一段时间后才真正发送请求,避免频繁的请求操作。

  2. 表单输入验证:在表单输入过程中,每次用户输入都可能触发验证操作。使用防抖函数可以延迟触发验证操作,只在用户输入完毕一段时间后进行验证,避免频繁的验证操作。

  3. 浏览器窗口调整事件:当用户调整浏览器窗口大小时,会触发resize事件。使用防抖函数可以延迟resize事件的触发,只在用户停止调整窗口一段时间后才执行对应的操作,避免频繁的计算和布局操作。

  4. 鼠标移动事件:在一些特定的交互场景中,需要根据鼠标的移动位置做出相应的交互。使用防抖函数可以延迟鼠标移动事件的触发,只在用户停止移鼠标一段时间后才执行相应的操作,避免过度频繁的操作。

以用户搜索框实时搜索请求后台为例,我们来详细看一下:

原始代码,未进行防抖优化

<div class="box">
  输入事件没有进行防抖处理:<input type="text" id="demo" name="demo">
</div>
<script>
  // 模拟请求
  function req(value){
    console.warn("request: " + value + ", time: " + new Date());
  }

  const inputBox = document.getElementById("demo");
  inputBox.addEventListener("keyup",e=>{
    req(e.target.value);
  })
</script>

0914-1.gif

上面结果所示,只要我们在输入框中每次输入文字,那么就会触发一次模拟请求,这对于用户和开发者而言都是不好的体验和资源的浪费。

思考: 我们想到每次用户输入文字都是需要一定时间的,那么我们可以定义在规定时间进行完整输入才能进行请求,这样我们可以减轻对后台的压力。

使用防抖函数优化后

防抖规则:500ms内输入文字按下键盘都不会触发请求事件,而是在输入框的定时器500ms停止输入后发送请求

优化: 我们改造一下上述的代码,监听我们的输入框,在500ms内连续输入,不进行任何操作,500ms后发送一次请求。

<div class="box">
  输入事件进行防抖处理:<input type="text" id="demo" name="demo">
</div>
<script>
  // 模拟请求
  function req(value){
    console.warn("request: " + value + ", time: " + new Date());
  }

  const inputBox = document.getElementById("demo");
  inputBox.addEventListener("keyup",e=>{
    debounce(() => req(e.target.value), 500);
  })
</script>

0914-1.gif

从上面的运行结果可以看出,在500ms内在输入框中连续输入文字都不会触发请求事件,而是在输入框的定时器500ms停止输入后发送请求。

实现原理很简单,就是对于频繁输入的输入框请求事件添加定时器进行计数,在指定时间内进行频繁输入并不会进行请求,而是在指定时间间隔内停止输入才会执行函数。

当停止输入但在此定时器计数时间内,会重新进行触发请求事件。

四. 节流函数的应用场景

  1. 页面滚动事件:当用户滚动页面时,会频繁触发滚动事件。使用节流函数可以控制滚动事件的触发频率,避免过多的计算和渲染操作,提高页面的性能和流畅度。

  2. 窗口大小调整:当用户调整浏览器窗口大小时,会不断触发resize事件。使用节流函数可以限制resize事件的触发频率,以避免过度计算和布局操作。

  3. 频繁点击按钮:在某些场景下,点击按钮可能会触发重复的提交操作。使用节流函数可以限制按钮点击的触发频率,防止重复的提交。

  4. 动画场景: 避免短时间内多次触发动画引起性能问题

  5. 拖拽场景: 在某些场景下,频繁触发位置变动会造成性能问题,固定时间内只执行一次,防止超高频次触发位置变动

以频繁点击按钮防止重复提交为例子,我们来详细看一下:

原始代码,未进行节流优化

<button id="demo" style="margin: 50px;">点击按钮</button></button>
<script>
    let value = 1
    // 模拟请求
    function req(){
        console.warn("request: " + value++ + ", time: " + new Date());
    }
    const ele = document.getElementById("demo");
    ele.addEventListener("click", (e) => {
        req()
    });
</script>

0914-1.gif

上面结果所示,只要我们点击按钮,那么就会触发一次模拟请求,这除了对于服务器的压力以外,还会造成多次数据提交,有可能会造成数据重复的风险。

使用节流函数优化后

节流规则:1000ms内频繁点击按钮,只能成功一次

优化: 按钮频繁多次点击时,为了避免用户多次请求,做了节流限制,在规定一个1000ms时间内,只能有一次点击成功的触发动作。

<button id="demo" style="margin: 50px;">点击按钮</button></button>
<script>
    let value = 1
    // 模拟请求
    function req(){
        console.warn("request: " + value++ + ", time: " + new Date());
    }
    const ele = document.getElementById("demo");
    ele.addEventListener("click", (e) => {
        throttle(() => req(), 1000)
    });
</script>

0914-1.gif

从上面的运行结果可以看出,在1000ms内按钮连续多次点击,只有一次成功。

五. 实现防抖函数和节流函数

实现防抖函数

实现思路

  1. 定义一个计时器变量,默认为null。
  2. 当事件触发时,清除之前的计时器。
  3. 创建一个新的计时器,延迟执行目标函数。
  4. 在在此时间内,如果再次触了事件,则重复步骤2和3。
  5. 在延迟时间内没有再次触发事件时,执行目标函数。
/**
 * @desc 防抖函数:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
 * @param {Function} func 函数
 * @param {Number} wait 延迟执行毫秒数
 * @param {Boolean} immediate true 表示立即执行,false 表示非立即执行
 */
let timeout

function debounce(func, wait = 500, immediate = false) {
  // 清除定时器
  if (timeout) clearTimeout(timeout)
  // 立即执行,此类情况一般用不到
  if (immediate) {
    let callNow = !timeout
    timeout = setTimeout(() => {
      timeout = null
    }, wait)
    if (callNow) typeof func === 'function' && func()
  } else {
    // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
    timeout = setTimeout(() => {
      typeof func === 'function' && func()
    }, wait)
  }
}

实现节流函数

实现思路

  1. 定义一个标记变量来表示是否允许执行目标函数,默认为0。
  2. 当事件触发时,检查当前的时间戳与标记变量的差值,如果差值大于设定的延迟时间,则执行函数并将标记变量设为当前的时间戳。如果差值小于设定的延迟时间,则不执行。
  3. 在指定时间间隔内再次触发事件时,则重复2步骤。
/**
 * @desc 节流函数:在一定时间内,只能触发一次
 * @param {Function} func 函数
 * @param {Number} wait 延迟执行毫秒数
 */
let previous = 0
function throttle(func, wait = 500) {
  let now = Date.now()
  if (now - previous > wait) {
    typeof func === 'function' && func()
    previous = now
  }
}


总结

在本篇文章中,我们了解了JavaScript中的防抖和节流优化技术。这两种技术都是为了解决频繁触发的事件而产生的性能问题,通过限制事件触发的频率,提升页面性能和用户体验。

我们首先介绍了防抖和节流的定义和区别,防抖是指在一段时间内,只执行最后一次触发的事件,而节流是指一段时间内,固定间隔触发事件。然后,我们详细阐述了防抖和节流的原理和实现方式。

在实际应用中,我们经常遇到需要使用防抖和节流来优化用户交互、滚动事件、输入框输入等场景。通过合理地防抖和节流,我们可以减少冗余的计算和渲染,提高页面的流畅性和响应速度。

在文章的结尾,提供了一些常见的优化库和工具,方便开发者快速应用于实际开发中。

通过本文的学习,希望大家能够充分理解防抖和节流的原理,并且能够根实际情况合理应用于自己的项目,提升页面的性能和用户体验。

第三方库

  1. Lodash - 防抖
  2. Lodash - 节流

作者:anyup
链接:https://juejin.cn/post/7270532002733228068
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

所属分类: 前端开发
uni-app真机调试报错
短视频制作的工作流程分为5个步骤

小当家,帮您把生意越做越大!

小当家ISV,重庆APP开发,小程序开发,软件系统开发 地址:重庆市南岸区南坪万达广场写字楼2栋19-6 联系电话:023-81361879

ICP备案号:渝ICP备15003473-1 增值电信业务许可证: 渝B2-15003473 渝公网安备 50010802005103号

友情链接: APP定制开发  小程序定制开发  MagicShop商城系统  酒类行业解决方案 

重庆小当家互联网信息技术有限公司