Vue3 + Vite 中使用 Lodash-es 的防抖(debounce)详解
在 Vue3 + Vite 项目中,debounce
是 lodash-es 中最常用的功能之一,它可以帮助我们优化高频事件的处理。下面我将详细讲解 debounce 的使用方法,并提供一个完整的示例。
Debounce 核心概念
防抖(debounce) 是一种优化高频操作的技术。它会将多次连续的操作合并为一次,确保函数在指定时间间隔内只执行一次。
适用场景
-
搜索框输入建议
-
窗口大小调整事件
-
滚动事件处理
-
表单验证
在 Vue3 中使用 Debounce
安装 lodash-es
bash
复制
下载
npm install lodash-es
Debounce 参数详解
基本语法
javascript
复制
下载
const debouncedFunc = debounce(func, [wait=0], [options={}])
参数说明
-
func
(Function): 要防抖的函数 -
[wait=0]
(number): 延迟的毫秒数 -
[options={}]
(Object): 选项对象 -
[options.leading=false]
(boolean): 指定在延迟开始前调用 -
[options.trailing=true]
(boolean): 指定在延迟结束后调用 -
[options.maxWait]
(number): 设置 func 允许被延迟的最大时间
选项说明
-
leading: true (立即执行模式)
-
第一次触发时立即执行
-
后续在等待时间内触发不会执行
-
等待时间结束后再次触发会再次立即执行
-
-
trailing: true (延迟执行模式 - 默认)
-
第一次触发后开始计时
-
在等待时间内再次触发会重置计时器
-
等待时间结束后执行最后一次操作
-
-
maxWait (最大等待时间)
-
确保函数在指定时间内至少执行一次
-
即使连续触发也不会超过这个时间间隔
-
在 Vue3 中使用 Debounce 的最佳实践
1. 在 setup 中创建防抖函数
javascript
复制
下载
import { debounce } from 'lodash-es' // 在 setup 中创建防抖函数 const debouncedFunction = debounce(() => { // 你的逻辑 }, 300)
2. 组件卸载时取消防抖
javascript
复制
下载
import { onUnmounted } from 'vue' onUnmounted(() => { debouncedFunction.cancel() })
3. 在模板中使用
vue
复制
下载
<input @input="debouncedSearch" />
4. 处理带参数的函数
javascript
复制
下载
const debouncedSearch = debounce((searchTerm) => { // 使用 searchTerm }, 500) // 在事件处理中 const handleInput = (e) => { debouncedSearch(e.target.value) }
5. 使用立即执行模式(leading)
javascript
复制
下载
const handleClick = debounce(() => { // 处理点击 }, 1000, { leading: true, trailing: false })
注意事项
-
避免重复创建:不要在每次渲染时创建新的 debounce 函数,否则会失去防抖效果
-
组件卸载时取消:防止内存泄漏和意外执行
-
合理设置等待时间:
-
搜索建议:200-500ms
-
窗口调整:100-300ms
-
按钮点击:1000ms(防止重复提交)
-
-
与 async/await 一起使用:
javascript
复制
下载
const debouncedAsync = debounce(async (param) => { const result = await fetchData(param) // 处理结果 }, 300)
这个示例展示了在 Vue3 + Vite 项目中如何有效地使用 lodash-es 的 debounce 功能,涵盖了多种使用场景和配置选项。
<template>
<div class="container">
<h1>Lodash-es Debounce 使用演示</h1>
<div class="input-group">
<label>搜索输入 (500ms 防抖):</label>
<input
type="text"
v-model="searchTerm"
@input="handleSearchInput"
placeholder="输入搜索关键词..."
/>
<div class="search-result">搜索结果: {{ searchResults }}</div>
</div>
<div class="resize-group">
<div class="resize-box" :style="{ width: boxWidth + 'px' }">
调整窗口大小查看效果
</div>
<p>窗口大小: {{ windowSize.width }} x {{ windowSize.height }}</p>
</div>
<div class="button-group">
<button @click="handleButtonClick">快速点击我 (防抖处理)</button>
<p>点击次数: {{ clickCount }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { debounce } from 'lodash-es'
// 搜索相关状态
const searchTerm = ref('')
const searchResults = ref('')
// 防抖处理搜索输入
const handleSearchInput = debounce(() => {
searchResults.value = `正在搜索: "${searchTerm.value}"...`
console.log('执行搜索:', searchTerm.value)
}, 500)
// 窗口大小相关状态
const windowSize = ref({ width: window.innerWidth, height: window.innerHeight })
// 防抖处理窗口大小变化
const handleResize = debounce(() => {
windowSize.value = {
width: window.innerWidth,
height: window.innerHeight
}
console.log('窗口大小更新:', windowSize.value)
}, 300)
// 监听窗口大小变化
onMounted(() => {
window.addEventListener('resize', handleResize)
})
// 组件卸载时移除监听器
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
// 按钮点击相关状态
const clickCount = ref(0)
// 防抖处理按钮点击(带立即执行选项)
const handleButtonClick = debounce(() => {
clickCount.value += 1
console.log('按钮点击处理', clickCount.value)
}, 1000, { leading: true, trailing: false })
</script>
<style>
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #f5f7fa, #e4edf9);
min-height: 100vh;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 2rem;
border-bottom: 2px solid #3498db;
padding-bottom: 1rem;
}
.input-group, .resize-group, .button-group {
background: white;
padding: 1.5rem;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
margin-bottom: 2rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: #34495e;
}
input {
width: 100%;
padding: 0.8rem;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 1rem;
margin-bottom: 1rem;
transition: border 0.3s;
}
input:focus {
border-color: #3498db;
outline: none;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
.search-result {
padding: 1rem;
background-color: #e3f2fd;
border-radius: 6px;
font-weight: 500;
color: #2c3e50;
}
.resize-box {
height: 150px;
background: linear-gradient(45deg, #3498db, #9b59b6);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
font-weight: bold;
border-radius: 8px;
margin-bottom: 1rem;
transition: width 0.3s ease;
}
p {
color: #7f8c8d;
font-size: 0.95rem;
}
button {
background: #3498db;
color: white;
border: none;
padding: 0.8rem 1.5rem;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background 0.3s, transform 0.2s;
display: block;
width: 100%;
max-width: 300px;
margin: 0 auto;
}
button:hover {
background: #2980b9;
}
button:active {
transform: translateY(2px);
}
.button-group p {
text-align: center;
margin-top: 1rem;
font-size: 1.1rem;
font-weight: bold;
color: #2c3e50;
}
</style>