图片示例:

使用说明
- 绿色竖线代表原始事件触发
- 红色方块代表防抖函数执行
- 蓝色方块代表节流函数执行
- 可通过滑块调整延迟时间观察效果变化
该Vue项目通过可视化方式演示了防抖和节流的区别:
1. **交互式演示**:在触发区域移动鼠标可以直观看到三种处理方式的区别
2. **实时统计**:显示原始事件、防抖和节流的执行次数
3. **时间线可视化**:用不同颜色标记事件触发和函数执行
4. **参数调节**:可以动态调整防抖和节流的延迟时间
5. **代码展示**:提供完整的函数实现和使用示例
项目特点:
- 使用Vue 3 Composition API实现响应式数据绑定
- 提供防抖和节流的完整实现代码
- 支持自动演示模式便于观察效果
- 响应式设计适配各种屏幕尺寸
- 现代化UI设计,用户体验友好
防抖实现代码
// 防抖函数实现
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Vue中使用示例
export default {
data() {
return {
searchQuery: '',
searchResults: []
};
},
methods: {
handleSearch() {
// 模拟API调用
console.log('搜索:', this.searchQuery);
// 实际项目中这里会调用搜索API
}
},
created() {
this.debouncedSearch = debounce(this.handleSearch, 500);
},
watch: {
searchQuery() {
this.debouncedSearch();
}
}
}
节流实现代码
// 节流函数实现
function throttle(func, limit) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= limit) {
func.apply(this, args);
lastCall = now;
}
};
}
// Vue中使用示例
export default {
data() {
return {
scrollPosition: 0
};
},
methods: {
handleScroll() {
console.log('滚动位置:', this.scrollPosition);
// 实际项目中这里会处理滚动逻辑
}
},
mounted() {
this.throttledScroll = throttle(this.handleScroll, 500);
window.addEventListener('scroll', this.throttledScroll);
},
beforeUnmount() {
window.removeEventListener('scroll', this.throttledScroll);
}
}
完整代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue防抖与节流实战演示</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
.timeline-marker {
transition: all 0.3s ease;
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
</style>
</head>
<body class="bg-gray-50">
<div id="app">
<!-- 导航栏 -->
<nav class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4 py-3">
<div class="flex justify-between items-center">
<h1 class="text-xl font-bold text-gray-800">Vue防抖与节流实战</h1>
<div class="flex space-x-4">
<button @click="activeTab = 'demo'"
:class="activeTab === 'demo' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500'"
class="px-3 py-2 font-medium transition-colors">
<i class="fas fa-play-circle mr-2"></i>演示
</button>
<button @click="activeTab = 'code'"
:class="activeTab === 'code' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500'"
class="px-3 py-2 font-medium transition-colors">
<i class="fas fa-code mr-2"></i>代码
</button>
</div>
</div>
</div>
</nav>
<!-- 主内容区域 -->
<main class="max-w-7xl mx-auto px-4 py-8">
<!-- 演示模式 -->
<div v-if="activeTab === 'demo'" class="space-y-8">
<!-- 统计卡片 -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="gradient-bg text-white p-6 rounded-xl shadow-lg">
<h3 class="text-lg font-semibold mb-2">防抖计数器</h3>
<div class="text-3xl font-bold">{{ debounceCount }}</div>
<p class="text-blue-100 mt-2">停止触发后执行</p>
</div>
<div class="gradient-bg text-white p-6 rounded-xl shadow-lg">
<h3 class="text-lg font-semibold mb-2">节流计数器</h3>
<div class="text-3xl font-bold">{{ throttleCount }}</div>
<p class="text-blue-100 mt-2">固定间隔执行</p>
</div>
<div class="gradient-bg text-white p-6 rounded-xl shadow-lg">
<h3 class="text-lg font-semibold mb-2">原始计数器</h3>
<div class="text-3xl font-bold">{{ normalCount }}</div>
<p class="text-blue-100 mt-2">每次触发都执行</p>
</div>
</div>
<!-- 控制面板 -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex flex-wrap gap-4 justify-between items-center mb-6">
<h2 class="text-xl font-bold text-gray-800">事件触发演示</h2>
<div class="flex gap-4">
<button @click="resetCounters"
class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 transition-colors">
<i class="fas fa-redo mr-2"></i>重置
</button>
<button @click="toggleAutoMode"
:class="autoMode ? 'bg-green-500 hover:bg-green-600' : 'bg-blue-500 hover:bg-blue-600'"
class="px-4 py-2 text-white rounded-lg transition-colors">
<i class="fas fa-robot mr-2"></i>{{ autoMode ? '停止自动' : '自动演示' }}
</button>
</div>
</div>
<!-- 事件触发区域 -->
<div class="h-48 bg-gray-100 rounded-lg border-2 border-dashed border-gray-300 flex items-center justify-center cursor-pointer hover:bg-gray-200 transition-colors"
@mousemove="handleMouseMove"
id="triggerArea">
<p class="text-gray-600 text-center">
<i class="fas fa-mouse-pointer text-2xl mb-2 block"></i>
在此区域移动鼠标触发事件<br>
<span class="text-sm">延迟时间:{{ delay }}ms</span>
</p>
</div>
<!-- 时间线可视化 -->
<div class="space-y-6">
<div>
<h3 class="font-semibold text-gray-700 mb-3">原始事件时间线</h3>
<div class="h-16 bg-gray-50 rounded-lg p-2 relative overflow-hidden">
<div v-for="(marker, index) in normalTimeline"
:key="'normal-' + index"
class="timeline-marker absolute w-0.5 h-full bg-green-400"
:style="{ left: marker.position + '%' }">
</div>
</div>
<div>
<h3 class="font-semibold text-gray-700 mb-3">防抖时间线</h3>
<div class="h-16 bg-gray-50 rounded-lg p-2 relative overflow-hidden">
<div v-for="(marker, index) in debounceTimeline"
:key="'debounce-' + index"
class="timeline-marker absolute w-3 h-3 bg-red-500 rounded transform -translate-x-1.5 -translate-y-1.5"
:style="{ left: marker.position + '%' }">
</div>
</div>
<div>
<h3 class="font-semibold text-gray-700 mb-3">节流时间线</h3>
<div class="h-16 bg-gray-50 rounded-lg p-2 relative overflow-hidden">
<div v-for="(marker, index) in throttleTimeline"
:key="'throttle-' + index"
class="timeline-marker absolute w-3 h-3 bg-blue-500 rounded transform -translate-x-1.5 -translate-y-1.5"
:style="{ left: marker.position + '%' }">
</div>
</div>
</div>
</div>
<!-- 设置面板 -->
<div class="bg-white rounded-xl shadow-lg p-6">
<h3 class="font-semibold text-gray-700 mb-4">参数设置</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">防抖延迟时间</label>
<input type="range" min="100" max="2000" step="100" v-model="delay"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<div class="text-center text-gray-600 mt-1">{{ delay }}ms</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">节流间隔时间</label>
<input type="range" min="100" max="2000" step="100" v-model="throttleDelay"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<div class="text-center text-gray-600 mt-1">{{ throttleDelay }}ms</div>
</div>
</div>
</div>
</div>
<!-- 代码模式 -->
<div v-else class="space-y-6">
<!-- 防抖代码示例 -->
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="gradient-bg text-white p-4">
<h3 class="text-lg font-semibold">防抖实现代码</h3>
</div>
<div class="p-6 bg-gray-800 text-gray-100 overflow-x-auto">
<pre class="text-sm"><code>// 防抖函数实现
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Vue中使用示例
export default {
data() {
return {
searchQuery: '',
searchResults: []
};
},
methods: {
handleSearch() {
// 模拟API调用
console.log('搜索:', this.searchQuery);
// 实际项目中这里会调用搜索API
}
},
created() {
this.debouncedSearch = debounce(this.handleSearch, {{ delay }});
},
watch: {
searchQuery() {
this.debouncedSearch();
}
}
}</code></pre>
</div>
</div>
<!-- 节流代码示例 -->
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="gradient-bg text-white p-4">
<h3 class="text-lg font-semibold">节流实现代码</h3>
</div>
<div class="p-6 bg-gray-800 text-gray-100 overflow-x-auto">
<pre class="text-sm"><code>// 节流函数实现
function throttle(func, limit) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= limit) {
func.apply(this, args);
lastCall = now;
}
};
}
// Vue中使用示例
export default {
data() {
return {
scrollPosition: 0
};
},
methods: {
handleScroll() {
console.log('滚动位置:', this.scrollPosition);
// 实际项目中这里会处理滚动逻辑
}
},
mounted() {
this.throttledScroll = throttle(this.handleScroll, {{ throttleDelay }});
window.addEventListener('scroll', this.throttledScroll);
},
beforeUnmount() {
window.removeEventListener('scroll', this.throttledScroll);
}
}</code></pre>
</div>
</div>
<!-- 使用说明 -->
<div class="bg-blue-50 rounded-xl p-6 border border-blue-200">
<h3 class="font-semibold text-blue-800 mb-3">使用说明</h3>
<ul class="text-blue-700 space-y-2">
<li><i class="fas fa-circle text-green-500 mr-2"></i>绿色竖线代表原始事件触发</li>
<li><i class="fas fa-circle text-red-500 mr-2"></i>红色方块代表防抖函数执行</li>
<li><i class="fas fa-circle text-blue-500 mr-2"></i>蓝色方块代表节流函数执行</li>
<li><i class="fas fa-sliders-h mr-2"></i>可通过滑块调整延迟时间观察效果变化</li>
</ul>
</div>
</div>
</div>
</main>
</div>
<script>
const { createApp, ref, computed, watch, onMounted, onUnmounted } = Vue;
createApp({
setup() {
// 响应式数据
const activeTab = ref('demo');
const debounceCount = ref(0);
const throttleCount = ref(0);
const normalCount = ref(0);
const delay = ref(500);
const throttleDelay = ref(500);
const autoMode = ref(false);
const autoInterval = ref(null);
// 时间线数据
const normalTimeline = ref([]);
const debounceTimeline = ref([]);
const throttleTimeline = ref([]);
// 定时器引用
let debounceTimer = null;
let throttleLastExec = 0;
// 防抖函数实现
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
// 节流函数实现
const throttle = (func, limit) => {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= limit) {
func.apply(this, args);
lastCall = now;
}
};
};
// 事件处理
const handleMouseMove = (event) => {
// 原始事件计数
normalCount.value++;
addNormalMarker(event.clientX);
// 防抖处理
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
debounceCount.value++;
addDebounceMarker();
}, delay.value);
// 节流处理
const now = Date.now();
if (now - throttleLastExec >= throttleDelay.value) {
throttleCount.value++;
throttleLastExec = now;
addThrottleMarker();
}
};
// 添加时间线标记
const addNormalMarker = (clientX) => {
const position = (clientX / window.innerWidth) * 100;
normalTimeline.value.push({ position });
// 限制时间线长度
if (normalTimeline.value.length > 50) {
normalTimeline.value.shift();
}
};
const addDebounceMarker = () => {
const position = Math.random() * 90 + 5;
debounceTimeline.value.push({ position });
if (debounceTimeline.value.length > 10) {
debounceTimeline.value.shift();
}
};
const addThrottleMarker = () => {
const position = Math.random() * 90 + 5;
throttleTimeline.value.push({ position });
if (throttleTimeline.value.length > 10) {
throttleTimeline.value.shift();
}
};
// 重置计数器
const resetCounters = () => {
debounceCount.value = 0;
throttleCount.value = 0;
normalCount.value = 0;
normalTimeline.value = [];
debounceTimeline.value = [];
throttleTimeline.value = [];
clearTimeout(debounceTimer);
throttleLastExec = 0;
};
// 自动演示模式
const toggleAutoMode = () => {
autoMode.value = !autoMode.value;
if (autoMode.value) {
autoInterval.value = setInterval(() => {
const mockEvent = { clientX: Math.random() * window.innerWidth };
handleMouseMove(mockEvent);
}, 100);
} else {
clearInterval(autoInterval.value);
}
};
// 监听延迟时间变化
watch(delay, () => {
clearTimeout(debounceTimer);
});
// 生命周期
onMounted(() => {
console.log('Vue防抖节流演示已加载');
});
onUnmounted(() => {
clearTimeout(debounceTimer);
clearInterval(autoInterval.value);
});
return {
activeTab,
debounceCount,
throttleCount,
normalCount,
delay,
throttleDelay,
autoMode,
normalTimeline,
debounceTimeline,
throttleTimeline,
handleMouseMove,
resetCounters,
toggleAutoMode
};
}
}).mount('#app');
</script>
</body>
</html>
495

被折叠的 条评论
为什么被折叠?



