Vue防抖与节流实战demo+完整代码

图片示例:

使用说明

  • 绿色竖线代表原始事件触发
  • 红色方块代表防抖函数执行
  • 蓝色方块代表节流函数执行
  • 可通过滑块调整延迟时间观察效果变化

该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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Technical genius

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值