防抖与节流:Vue3+TS 实战指南
作为一个常被产品经理吐槽"页面卡成PPT"的程序员,今天带你彻底掌握两个让页面飞起来的神器:节流(Throttle) 和 防抖(Debounce) 。本文使用 Vue3 + TypeScript + Vite + VueUse 实战演示,无需造轮子,直接上手!
一、为什么页面会卡顿?
先看两个典型场景:
-
搜索框场景
用户每输入一个字母就发起请求,输入"前端开发"(4字)触发 8 次请求 → 服务器压力巨大
-
滚动加载场景
用户滚动鼠标时,代码疯狂计算,页面卡顿如PPT
解决方案:节流和防抖就像"交通管理员",精准控制请求频率,让页面丝滑如丝绸。
二、核心区别
🧠 核心区别:执行时机不同
| 特性 | 防抖 (Debounce) | 节流 (Throttle) |
|---|---|---|
| 执行时机 | 最后一次触发后等待一段时间才执行 | 每隔一段时间执行一次 |
| 执行次数 | 无论触发多少次,最终只执行1次 | 每个时间间隔内最多执行1次 |
| 触发逻辑 | "等你停手了,我再行动" | "按固定频率执行,拒绝插队" |
| 典型比喻 | 电梯关门:最后一个人进来后3秒关门 | 地铁发车:每5分钟一班,必须等5分钟 |
💡 关键理解:
防抖关注"最终结果",节流关注"执行频率"
(搜索要结果,滚动要过程)
三、场景选择
| 场景 | 推荐方式 | 为什么? | 实际案例 |
|---|---|---|---|
| 搜索框输入 | ✅ 防抖 | 用户输入结束再请求,避免无效请求 | 电商网站搜索框 |
| **页面滚动 ** | ✅ 节流 | 需持续响应滚动动作 | 无限滚动加载 |
| 按钮点击 | ✅ 节流 | 防止用户快速连续点击 | 提交按钮、点赞按钮 |
| 表单实时验证 | ✅ 防抖 | 输入结束再验证,避免干扰用户 | 用户名/邮箱格式校验 |
| 实时输入监听 | ✅ 防抖 | 需要实时响应输入变化 | 密码强度提示 |
💡 一句话总结:
要结果用防抖,要过程用节流
(搜索要结果,滚动要过程)
四、实战
✅ 无需手写,对于Vue3工程,可以直接使用 VueUse 组件
bash
npm install @vueuse/core
1. 防抖(Debounce)
1. 使用示例
vue
<template>
<div>
<el-input
v-model="keyword"
@blur="debouncedSearch"
placeholder="搜索点什么..."
style="width: 300px"
/>
<p>搜索关键词:{{ keyword }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useDebounceFn } from '@vueuse/core'
const keyword = ref('')
const searchAPI = () => console.log('发起搜索请求时间:', new Date().toLocaleString())
const debounce = useDebounceFn(() => searchAPI(), 1000)
const debouncedSearch = () => {
debounce()
console.log('失去焦点的时间:', new Date().toLocaleString())
}
</script>

2. useDebounceFn函数用法

| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
fn |
(...args: any[]) => any |
必填 | 需要防抖的原始函数 |
ms |
number |
0 |
防抖延迟(毫秒),停手多少 ms 后才执行 fn |
options |
DebounceFilterOptions |
见下方 | 高级行为配置对象(可选) |
DebounceFilterOptions 子项
| 选项名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
maxWait |
number |
undefined |
最大等待时间,超时必执行 |
rejectOnCancel |
boolean |
false |
手动 cancel() 后,promise() 是否 reject 而不是一直 pending |
返回值附加方法
| 方法名 | 含义 |
|---|---|
debounced(...args) |
正常触发,防抖生效 |
debounced.cancel() |
取消当前计时,不会执行原函数 |
debounced.flush() |
立即执行原函数并清空计时 |
debounced.promise(...args) |
返回 Promise,resolve 原函数返回值 |
2. 节流(Throttle)
1. 使用示例
vue
<template>
<div>
<el-button type="primary" @click="throttleSearch">点击</el-button>
</div>
</template>
<script setup lang="ts">
import { useThrottleFn } from '@vueuse/core'
const searchAPI = () => console.log('发起搜索请求时间:', new Date().toLocaleString())
const throttle = useThrottleFn(() => searchAPI(), 1000)
let count = 0
const throttleSearch = () => {
throttle()
console.log(`点击了${++count}次`)
}
</script>

2. useThrottleFn函数用法

| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
fn |
(...args: any[]) => any |
必填 | 需要被节流包装的原始函数 |
ms |
number |
0 |
节流间隔(毫秒),在该时间段内最多执行一次 fn |
options |
ThrottleFnOptions |
见下方 | 高级行为配置对象(可选) |
ThrottleFnOptions 子项
| 选项名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
leading |
boolean |
true |
是否在节流周期开始立即执行一次原函数 |
trailing |
boolean |
false |
是否在节流周期结束再执行一次原函数 |
rejectOnCancel |
boolean |
false |
手动 cancel() 后,promise() 是否 reject |
返回值函数
| 方法名 | 含义 |
|---|---|
throttled(...args) |
正常触发,节流生效 |
throttled.cancel() |
取消当前计时,不会执行原函数 |
throttled.flush() |
立即执行原函数并清空计时 |
throttled.promise(...args) |
返回 Promise,resolve 原函数返回值 |
五、口诀
| 技术 | 核心思想 | 适用场景 | 一句话总结 |
|---|---|---|---|
| 防抖 | 等你停手了,我再行动 | 搜索、表单验证 | 要结果,别急着动 |
| 节流 | 按固定频率执行 | 滚动、拖拽 | 要过程,别太急 |
总结
节流和防抖是前端性能的"刹车系统":
✅ 用对 = 页面丝滑如丝绸
❌ 用错 = 服务器崩溃+产品经理暴怒
建议收藏本文,下次产品说"页面卡"时,先检查是否忘了加防抖/节流! 😄
| 节流 | 按固定频率执行 | 滚动、拖拽 | 要过程,别太急 |
总结
节流和防抖是前端性能的"刹车系统":
✅ 用对 = 页面丝滑如丝绸
❌ 用错 = 服务器崩溃+产品经理暴怒
建议收藏本文,下次产品说"页面卡"时,先检查是否忘了加防抖/节流! 😄