【高频触发事件优化】封装节流和防抖

简介: 【高频触发事件优化】封装节流和防抖

数字化管理平台

Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus

Vue权限系统案例

个人博客地址

最近项目有一个数据导出功能,之前后端都是同步处理,前端做防抖处理,数据量特别大的时候,造成响应时间特别长。这次后端做了优化处理,将后端做了异步处理,响应特别快,但是需要再额外的下载页面去处理导出,这样造成用户下载后直接进入下载页面需要等待一段时间才能看到下载文件,而且对于后端来讲,防抖虽然可以控制频率(默认1s),但是他想要把时间控制在 10s 左右,才能实现二次下载。如果继续使用防抖,如果直接定 10s ,就会造成第一次点击无效的点击,当然这个可以通过一个变量动态控制这个时间,但是稍显麻烦。所以我采用了节流,去实现他的需求,相对简单了很多。

函数防抖和函数节流都是防止某一时间频繁触发,但是这两兄弟之间的原理却不一样。具体使用哪一个需要大家根据实际情况决定。

5715556ffaab4704885e43399091aa4c.png函数节流与函数防抖是解决频繁触发DOM事件的两种常用解决方案:

  1. 1.**基本思想:**某些代码不可以在没有间断的情况连续重复执行。
  2. 2. 使用原因:DOM 操作比起非 DOM 交互,需要更多的内存和 CPU 时间,连续尝试过多的 DOM 操作可能会导致浏览器挂起,甚至崩溃。
  3. 3. **应用场景:**只要代码是周期执行的,都应该节流。鼠标移动 mousemove 事件、滚动条滚动 scroll 事件,浏览器窗口改变 resize 事件等。
  4. 4. 防抖节流逻辑图

e57b24943b654b8db216982500849100.png

5.1 节流器(Throttle)

节流:在规定时间内,只触发一次。比如我们设定500ms间隔,在这个时间段内,无论点击按钮多少次,它都只会触发一次。

生活举例:一个水龙头在滴水,可能一次性会滴很多滴,但是我们只希望它每隔 500ms 滴一滴水,保持这个频率。即我们希望函数在以一个可以接受的频率重复调用。

具体场景:如 1.商品抢购环节,由于有无数人快速点击按钮,如果每次点击都发送请求,就会给服务器造成巨大的压力。2. 数据导出功能,当数据量特别大,一次点击后端需要耗费大量时间的处理才能响应数据,如果频繁点击导出按钮,严重可能会造成服务器崩溃。如果对以上场景进行节流处理,就会大大减少请求的次数,从而减轻服务器压力。

核心:在于让一个函数不要执行的太频繁,减少一些过快的操作,类似于王者荣耀中技能冷却时间。

5.1.1 使用时间戳封装节流器

/**
 * @param {*} fn  需要包装的事件回调
 * @param {*} delay 每次推迟执行的等待时间
 */
export function throttle(fn, delay = 1000) {
  // 距离上一次的执行时间
  let latestTime = 0
  return function () {
    const _this = this
    const _arguments = arguments
    const nowTime = new Date().getTime()
    // 如果距离上次执行超过了,delay才能再次执行
    if (nowTime - latestTime > delay) {
      fn.apply(_this, _arguments)
      latestTime = nowTime
    } else {
      this.$message.warning(`下载频率过高,请${Math.ceil((delay - (nowTime - latestTime)) / 1000)}秒后再次下载!`)
    }
  }
}

时间戳版本会优先执行,点击立即执行一次。也是这次项目导出功能优化采用的方式。

5.1.2 使用定时器封装节流器

export function throttle(fn, delay = 1000) {
  let timer = null
  return function () {
    const _this = this
    const _arguments = arguments
    if (!timer) {
      timer = setTimeout(function () {
        timer = null;
        fn.apply(_this, _arguments)
      }, delay)
    }
  }
}

定时器版本会后置执行,点击需等待 delay 时间之后执行。之前做的防抖就是这个样子,后置执行的情况下,设置过长等待时间体验不好。

5.2 防抖(Debounce)

防抖:在连续的操作中,无论进行了多长时间,只有某一次的操作后在指定的时间内没有再操作,这一次才被判定有效。

生活举例:将一个弹簧按下,继续加压,继续按下,只会在最后放手的一瞬反弹。即我们希望函数只会调用一次,即使在这之前反复调用它,最终也只会调用一次而已。

具体场景:搜索框输入关键字过程中实时请求服务器匹配搜索结果,如果不进行处理,那么就是输入框内容一直变化,导致一直发送请求。如果进行防抖处理,结果就是当我们输入内容完成后,一定时间(比如500ms)没有再输入内容,这时再触发请求。

核心:在短时间内大量触发同一事件时,只会执行一次回调函数。避免把一次事件误认为多次。

封装防抖函数

/**
 * @param {*} fn  需要包装的事件回调
 * @param {*} delay 每次推迟执行的等待时间
 */
export function debounce(fn, delay = 1000){
  // 定时器
  let timer = null
  // 将debounce处理结果当作函数返回
  return function() {
    // 保留调用时的 this 上下文
    const _this = this
    // 保留调用时传入的参数
    const _arguments = arguments
    // 每次事件被触发时,都去清除之前的旧定时器
    if (timer) {
      clearTimeout(timer)
    }
    // 设立新定时器
    timer = setTimeout(function() {
      fn.apply(_this, _arguments)
    }, delay)
  }
}

5.3 常见使用场景

监听 scroll、mousemove 等事件 - 节流(每隔一秒计算一次位置)

监听浏览器窗口 resize 操作 - 防抖(只需计算一次)

键盘文本输入的验证 - 防抖(连续输入文字后发送请求进行验证,验证一次就好)

提交表单 - 防抖(多次点击变为一次)

search 搜索联想,用户在不断输入值时,用防抖来节约请求资源。

登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖

这里,以 resize 窗口改变事件 为例,以原生代码演示节流和防抖效果:

未使用防抖和节流的代码

window.onresize = function() {  
  var div = document.getElementById('mydiv')
  div.style.height = div.offsetWidth + 'px'
  console.log('resize')
}

不停的改变窗口大小,观察打印结果。。。。

很明显,用户如果不断放大缩小浏览器窗口,那我们监听函数将会不停的被调用,倘若函数过“重”,即假设如上文描述的一般,那么对浏览器的压力将会非常之大,其高频率的更改可能会让浏览器崩溃。

防抖处理

function debounce(fn, delay = 1000) {  
    var timer = null;
    return function() {
        if(timer) {
          clearTimeout(timer)
        }
        var _arguments = arguments
        var _this = this
        timer = setTimeout(function() {
            fn.call(_this, _arguments)
        }, delay)
  }
}
function resizeDiv() {  
    var div = document.getElementById('mydiv')
    div.style.height = div.offsetWidth + 'px'
    console.log('resize');
}
window.onresize = debounce(resizeDiv)

触发事件,查看和之间的比较,确实解决了那个问题,但是,

放手之后这里才变化,显得有点突兀,那么能不能在我拖动的时候不能也让它变化同步呢???

节流处理

function resizeDiv(){
    var div = document.getElementById('myDiv')
    div.style.height = div.offsetWidth + "px";
}
function throttle(fn, delay = 1000){
    clearTimeout(fn.timer);
    fn.timer = setTimeout(x=>{
        fn.call();
    }, delay)
}
window.onresize = function(){
    throttle(resizeDiv)
}

控制好处理频率,可以确保浏览器不会在极短的时间内进行过多次的计算。显然函数节流比防抖更适合窗口的高频变化。

相关文章
|
11月前
|
存储 传感器 监控
未来城市的智慧交通系统
智慧交通系统是未来城市发展的重要组成部分,通过整合物联网、大数据和人工智能技术,实现交通的智能化管理。本文探讨了智慧交通系统的关键技术、架构及其在实际应用中的案例,并展望了未来的发展趋势。
|
11月前
|
缓存 移动开发 JavaScript
《vue2进阶篇:路由》第10章:vue-router,包括基础路由、嵌套路由、路由的query参数和params参数、命名路由、router-link的replace属性、编程式路由、缓存路由组件
《vue2进阶篇:路由》第10章:vue-router,包括基础路由、嵌套路由、路由的query参数和params参数、命名路由、router-link的replace属性、编程式路由、缓存路由组件
562 2
|
11月前
|
前端开发 JavaScript
Async/Await 如何通过同步的方式(形式)实现异步
Async/Await 是一种在 JavaScript 中以同步方式书写异步代码的语法糖。它基于 Promise,使异步操作看起来更像顺序执行,简化了回调地狱,提高了代码可读性和维护性。
|
10月前
|
前端开发 Java 测试技术
多商户入驻系统开发源码案例
多商户入驻系统的开发涉及需求分析、系统架构设计、开发实现、测试优化及部署上线等关键步骤。项目需明确核心功能,选择合适的技术栈,确保系统的稳定性、扩展性和用户体验,最终实现商业目标和长期成功。
|
JavaScript API
【Vue 3】推荐 1 个简约且美丽的天气组件
【Vue 3】推荐 1 个简约且美丽的天气组件
|
人工智能 自然语言处理 语音技术
使用AI识别语音和B站视频并通过GPT生成思维导图原创
AI脑图现新增语音及B站视频内容识别功能,可自动生成思维导图。用户可通过发送语音或上传语音文件,系统自动转换为文本并生成结构化的思维导图;对于B站视频,仅需提供链接即可。其工作流程包括:语音转文本、文本结构化、生成Markdown、Markdown转思维导图HTML以及输出最终的思维导图图片给用户。
685 0
|
数据安全/隐私保护 Docker 容器
docker 部署nexus
要在Docker上部署Nexus,可以按照以下步骤进行操作: 1. 确保已经安装并配置好Docker。可以在官方网站(https://www.docker.com/)上找到适合你操作系统的安装程序,并按照说明进行安装。 2. 搜索并下载Nexus的Docker镜像。在Docker Hub上搜索"Nexus",找到Sonatype官方提供的Nexus Repository Manager的镜像。 3. 使用以下命令从Docker Hub上下载Nexus镜像: ``` docker pull sonatype/nexus3 ``` 4. 运行Nexus容器。使用以下命令创建并运行一个名为"
1308 0
|
存储 小程序 前端开发
毕业设计:微信小程序健康管理系统的开发与实现
经过调查和走访研发的这套在线健康管理系统,采用微信小程序云开发实现,基于Mongodb数据库进行数据存储,前端使用微信小程序开发实现,后端基于Nodejs开发实现。系统前端用户主要实现查看新闻通知、体检知识、在线体检预约、查看我的预约、修改个人资料等。后台主要实现用户管理、内容管理、活动和预约管理、统计预约用户数等功能。这套系统的上线对于人们健康的管理和体检预约都有很大帮助。...
3682 2
毕业设计:微信小程序健康管理系统的开发与实现
|
JavaScript
vue列表信息展示中新增数据,与编辑数据页面复用,降低重复代码
vue列表信息展示中新增数据,与编辑数据页面复用,降低重复代码
528 2
|
网络协议 Linux
【系统DFX】如何诊断占用过多 CPU、内存、IO 等的神秘进程?
【系统DFX】如何诊断占用过多 CPU、内存、IO 等的神秘进程?
253 0