当前位置: 首页 > news >正文

HarmonyOS ClickUtil 节流与防抖:彻底搞懂按钮防重复点击

目录

    • 概述
    • 一、为什么需要节流和防抖?
    • 二、节流 vs 防抖,傻傻分不清?
    • 三、ClickUtil 源码解析
      • 3.1 核心状态
      • 3.2 throttle — 节流实现
      • 3.3 debounce — 防抖实现
    • 四、完整 Demo 演示
      • 4.1 节流 Demo
      • 4.2 防抖 Demo
      • 4.3 节流 UI
      • 4.4 防抖 UI
    • 五、运行效果对比
    • 六、如何选择节流还是防抖?
    • 七、API 速查表
    • 八、小结

概述

近期发现一款很有意思的HarmonyOS 三方库, 地址 @pura/harmony-utils(V1.4.0) , 作者是"桃花镇童长老", 我这里也是直接通过该作者公布的源码进行案例编写进行,写了到目前写了一部分demo ,感觉确实很有帮助,这里呢也是开始写一个系列的演示demo 供大家参考。如有帮助可以在OpenHarmony中进行下载安装进行使用哦

案例demo导航展示

↓↓↓↓↓↓接下来言归正传 ↓↓↓↓

一、为什么需要节流和防抖?

在移动应用中,用户往往会在极短时间内连续点击按钮。如果没有任何防护措施:

  • 重复提交表单:用户点了"提交"后因网络慢又点了几次,服务器收到多个相同请求
  • 重复触发导航:点击跳转按钮连续触发,路由栈里压了多个相同页面
  • 接口被刷:搜索框每次击键都请求接口,每秒钟可能发起几十个请求

节流(Throttle)防抖(Debounce)是解决这类问题的经典方案。

二、节流 vs 防抖,傻傻分不清?

用一个形象的比喻来区分:

节流:就像地铁的闸机,在规定时间内只让一个人通过,后面的人必须等前一个人完全通过后才能进入。适合"限制频率"的场景。

防抖:就像电梯门,只要还有人进来,电梯门就一直等待。直到没有人进来(停止操作),等待一段时间后才关门执行。适合"等待停止"的场景。

节流(Throttle)防抖(Debounce)
核心逻辑固定时间内只执行一次最后一次操作后延迟执行
触发时机立即执行或延迟执行停止操作后执行
适合场景防重复提交、滚动事件搜索输入、窗口缩放

三、ClickUtil 源码解析

3.1 核心状态

exportclassClickUtil{privatestaticthrottleTimeoutID:number;//节流timeoutIDprivatestaticflag:boolean=false;//节流flag,true=已经进入执行状态了privatestaticdefaultId:string=DateUtil.getTodayTime().toString();//防抖Id
  • throttleTimeoutID:存储节流定时器的 ID,用于在等待期结束后重置flag
  • flag:节流的门控标志,true表示正在节流冷却期
  • defaultId:防抖的默认 ID(用时间戳初始化),当只有一个防抖事件时使用

3.2 throttle — 节流实现

staticthrottle(func:()=>void,wait:number=1000,immediate:boolean=true){if(immediate){if(!ClickUtil.flag){ClickUtil.flag=true;typeoffunc==='function'&&func();ClickUtil.throttleTimeoutID=setTimeout(()=>{ClickUtil.flag=false;clearTimeout(ClickUtil.throttleTimeoutID);},wait);}}else{if(!ClickUtil.flag){ClickUtil.flag=true;ClickUtil.throttleTimeoutID=setTimeout(()=>{ClickUtil.flag=false;typeoffunc==='function'&&func();clearTimeout(ClickUtil.throttleTimeoutID);},wait);}}}

参数说明:

参数类型默认值说明
func() => void必填要执行的回调函数
waitnumber1000节流冷却时间(毫秒)
immediatebooleantruetrue=立即执行后冷却,false=冷却结束后执行

两种模式的时序对比:

immediate = true(立即模式):

点击 → 立即执行 → 冷却 1000ms → 可以再次点击

immediate = false(延迟模式):

点击 → 冷却 1000ms → 执行 → 可以再次点击

3.3 debounce — 防抖实现

staticdebounce(func:()=>void,wait:number=1000,clickId:string=ClickUtil.defaultId){letcacheID=CacheUtil.get<number>(`ClickUtil_debounce_timeoutID_${clickId}`);//获取idif(cacheID!==undefined&&cacheID!==null){clearTimeout(cacheID);}lettimeoutID=setTimeout(()=>{typeoffunc==='function'&&func();clearTimeout(timeoutID);},wait);CacheUtil.put<number>(`ClickUtil_debounce_timeoutID_${clickId}`,timeoutID);//缓存id}

参数说明:

参数类型默认值说明
func() => void必填要执行的回调函数
waitnumber1000防抖等待时间(毫秒)
clickIdstringdefaultId事件标识,用于区分多个独立的防抖事件

防抖的工作原理:

  1. 每次触发时,先从CacheUtil中取出上一次的timeoutID
  2. 如果存在,调用clearTimeout取消上一次的定时器
  3. 重新设置一个新的定时器
  4. 将新的timeoutID存入CacheUtil

这样,只要在wait毫秒内持续点击,定时器一直被重置;只有停止点击并等待wait毫秒后,函数才会执行。

多事件 ID 的设计:

通过clickId参数,可以维护多个独立的防抖事件:

// 搜索框防抖ClickUtil.debounce(()=>this.doSearch(),500,'search_input');// 评论提交防抖ClickUtil.debounce(()=>this.submitComment(),1000,'comment_submit');

两个事件在CacheUtil中的 key 分别是:

  • ClickUtil_debounce_timeoutID_search_input
  • ClickUtil_debounce_timeoutID_comment_submit

互不干扰。

四、完整 Demo 演示

来自CacheCharClickDemoPage.ets的 ClickUtil 演示部分:

4.1 节流 Demo

@StatethrottleCount:number=0;@StatethrottleImmediate:boolean=true;doThrottle(){ClickUtil.throttle(()=>{this.throttleCount++;this.addLog('Throttle',`触发!累计:${this.throttleCount}`,'success');},1500,this.throttleImmediate);}

4.2 防抖 Demo

@StatedebounceCount:number=0;@StatedebounceDelay:number=1000;doDebounce(){ClickUtil.debounce(()=>{this.debounceCount++;this.addLog('Debounce',`执行!累计:${this.debounceCount}`,'success');},this.debounceDelay,'demo_debounce');}

4.3 节流 UI

// 模式选择Row(){Text('触发模式:').fontSize(12).fontColor('#888')Radio({value:'imm',group:'throttleMode'}).checked(this.throttleImmediate).onChange((v:boolean)=>{if(v)this.throttleImmediate=true;})Text('立即执行').fontSize(12).margin({left:2}).onClick(()=>{this.throttleImmediate=true;})Radio({value:'delayed',group:'throttleMode'}).checked(!this.throttleImmediate).onChange((v:boolean)=>{if(v)this.throttleImmediate=false;})Text('延迟执行').fontSize(12).margin({left:2}).onClick(()=>{this.throttleImmediate=false;})}.margin({bottom:10})// 触发按钮Button('快速连续点击我(节流)').width('100%').height(48).borderRadius(10).backgroundColor('#4080FF').fontColor('#FFF').fontSize(14).onClick(()=>{this.doThrottle();})

4.4 防抖 UI

// 延迟时间调节Row(){Text('延迟时间:').fontSize(12).fontColor('#888')Slider({value:this.debounceDelay,min:300,max:3000,step:100,style:SliderStyle.OutSet}).layoutWeight(1).showTips(true).onChange((v:number)=>{this.debounceDelay=v;})Text(`${this.debounceDelay}ms`).fontSize(12).fontColor('#4080FF').margin({left:8})}.margin({bottom:10})// 触发按钮Button('快速连续点击我(防抖)').width('100%').height(48).borderRadius(10).backgroundColor('#FF9800').fontColor('#FFF').fontSize(14).onClick(()=>{this.doDebounce();})

五、运行效果对比

节流测试(immediate=true,间隔 1500ms):

在 1.5 秒内快速点击 10 次,日志只会出现 1 条:

[Throttle] 触发!累计: 1 次

等 1.5 秒后再次点击,才会新增一条日志。

防抖测试(延迟 1000ms):

快速点击 10 次,不会立即有日志。停止点击,等 1 秒后才出现 1 条:

[Debounce] 执行!累计: 1 次

六、如何选择节流还是防抖?

场景推荐方案原因
提交按钮防重复节流(immediate=true第一次点击立即响应,后续冷却
搜索框实时搜索防抖等用户停止输入后才请求
页面滚动事件节流固定频率触发,不能太密集
窗口 resize防抖等缩放停止后再更新布局
点赞/收藏按钮节流(immediate=true第一次立即反馈,避免重复
验证码发送节流(较长wait60 秒内只能发送一次

七、API 速查表

方法参数说明
throttle(fn, wait, immediate)fn: 回调;wait: 间隔毫秒(默认1000);immediate: 是否立即执行(默认true)节流,固定时间窗口内只执行一次
debounce(fn, wait, clickId)fn: 回调;wait: 等待毫秒(默认1000);clickId: 事件ID防抖,停止触发后延迟执行

八、小结

ClickUtil将两种经典的交互优化方案封装为简洁的静态方法:

  • throttle:适合需要"限制频率"的场景,immediate参数控制是先执行还是后执行
  • debounce:适合需要"等待停止"的场景,clickId参数支持多个独立防抖事件

底层依赖CacheUtil存储防抖定时器 ID,这也体现了工具类之间协作的设计思想。在任何需要防止重复操作的场景,ClickUtil都是首选方案。

http://www.rkmt.cn/news/1388466.html

相关文章:

  • 禅道RCE漏洞原理与三阶修复实战指南
  • CNA BUSOFF 理解
  • AI时代,企业为什么需要重新理解“架构安全”?
  • Windows右键菜单终极管理方案:ContextMenuManager让效率提升300%
  • 基础知识:What are Skills?
  • 非遍历反常扩散随机游走模型分析与蒙特卡洛模拟【附代码】
  • LabVIEW规避数据竞争 保障线程稳定
  • 三维针刺材料多尺度力学仿真复现
  • 神经网络压缩技术在6G通信中的应用与优化
  • VLStream 视频 AI 融合平台介绍(2026 全开源版)
  • Python爬取Amazon实战:Playwright+动态请求头+Session池方案
  • AI代理成本优化:基于WhichModel的动态模型选择与智能路由实践
  • 深圳电磁屏蔽插箱厂家
  • 助睿实验作业3-学生用户画像-考勤主题扩展标签构建、可视化
  • AI原生转型:不造轮子,如何用现成方案重塑企业核心流程
  • 别再写“大灰狼吃小红帽”了!用LaTeX写CVPR论文,避开这些新手坑
  • BepInEx插件框架:让每个玩家都能成为游戏改造师
  • 百度网盘提取码一键查询:3步告别资源获取烦恼
  • 基于FPGA的USB-DMX场景控制器:从协议解析到硬件实现
  • 2026年中卫市本地上门黄金回收门店指南 彩金+铂金+金条+白银回收门店联系方式推荐 - 大熊猫898989
  • 2026年乌兰察布市本地上门黄金回收门店指南 彩金+铂金+金条+白银回收门店联系方式推荐 - 大熊猫898989
  • bili2text终极指南:三分钟将B站视频变文字稿的免费神器
  • 如何快速定位手机号码地理位置:终极开源工具使用指南
  • AzurLaneAutoScript终极指南:5分钟打造你的碧蓝航线智能管家
  • CentOS 7 OpenSSL 1.1.1 安全编译安装与动态库隔离实战
  • 从SSC到SEE:高通Sensor架构演进对Android驱动工程师意味着什么?
  • 告别MainTest!用XML文件在CANoe里勾选测试用例的保姆级教程(附避坑指南)
  • 从零到一:手把手教你用Playwright+Pytest+Yaml+Allure搭建一个能跑起来的UI自动化框架(保姆级避坑指南)
  • LLM推理系统优化:结构化输出与缓存管理技术解析
  • 别只看跑分!老将E5-2696V3搭RX580,实测多开手游模拟器到底卡不卡?