节流 和 防抖的使用

节流(Throttle)是一种常用的性能优化技术,用于限制函数的执行频率,确保在一定时间内只执行一次。它常用于处理浏览器事件(如滚动、窗口调整大小、鼠标移动等),以避免因事件触发过于频繁而导致的性能问题。

以下是手写节流函数的实现方法,以及一些常见的使用场景和注意事项。

节流函数的实现

节流函数的核心思想是:在指定的时间间隔内,只允许函数执行一次。以下是两种常见的节流实现方式

1. 时间戳版本(基于时间间隔)
ini 复制代码
function throttle(func, limit) {
  let lastFunc;
  let lastRan;
  return function () {
    const context = this;
    const args = arguments;
    if (!lastRan) {
      func.apply(context, args);
      lastRan = Date.now();
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(function () {
        if (Date.now() - lastRan >= limit) {
          func.apply(context, args);
          lastRan = Date.now();
        }
      }, limit - (Date.now() - lastRan));
    }
  };
}
2. 定时器版本(基于固定时间间隔)
ini 复制代码
function throttle(func, limit) {
  let inThrottle;
  return function () {
    const args = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

使用示例

1. 滚动事件
javascript 复制代码
window.addEventListener('scroll', throttle(function () {
  console.log('Scrolling...');
}, 100));
2. 窗口调整大小
javascript 复制代码
window.addEventListener('resize', throttle(function () {
  console.log('Resizing...');
}, 200));

注意事项

  1. 参数传递

    • 节流函数需要正确处理原函数的上下文(this)和参数(arguments),确保它们能够正确传递。
  2. 性能优化

    • 节流函数本身也会影响性能,因此需要合理设置时间间隔。
  3. 销毁定时器

    • 如果使用了定时器版本,记得在组件销毁时清除定时器,避免内存泄漏。

完整代码示例

以下是一个完整的节流函数实现,结合了时间戳和定时器的优点:

javascript 复制代码
function throttle(func, limit) {
  let inThrottle;
  let lastRan;
  return function () {
    const args = arguments;
    const context = this;
    if (!lastRan) {
      func.apply(context, args);
      lastRan = Date.now();
    } else {
      clearTimeout(inThrottle);
      inThrottle = setTimeout(function () {
        if (Date.now() - lastRan >= limit) {
          func.apply(context, args);
          lastRan = Date.now();
        }
      }, limit - (Date.now() - lastRan));
    }
  };
}

// 使用示例
window.addEventListener('scroll', throttle(function () {
  console.log('Scrolling...');
}, 100));

防抖(Debounce)是一种常用的性能优化技术,用于限制函数的执行频率。与节流(Throttle)不同,防抖的核心思想是:在指定的时间间隔内,只有最后一次触发事件时才执行函数。如果在时间间隔内再次触发事件,则重新计时。

防抖常用于以下场景:

  1. 输入框的搜索建议:避免用户输入时频繁触发搜索请求。
  2. 按钮点击:防止用户快速多次点击导致多次提交。
  3. 窗口调整大小:避免频繁触发布局调整。

以下是防抖函数的实现方法以及具体案例。


防抖函数的实现

防抖函数的实现相对简单,核心是利用 setTimeoutclearTimeout 来控制函数的执行。

基本实现
javascript 复制代码
function debounce(func, delay) {
  let timeoutId;
  return function (...args) {
    const context = this;
    clearTimeout(timeoutId); // 清除之前的定时器
    timeoutId = setTimeout(() => {
      func.apply(context, args); // 延迟后执行函数
    }, delay);
  };
}
实现思路
  1. 每次触发事件时,清除之前的定时器。
  2. 设置一个新的定时器,延迟指定时间后执行目标函数。
  3. 如果在延迟时间内再次触发事件,则重新计时。

具体案例:输入框搜索建议

假设我们有一个输入框,用户输入时需要触发搜索建议。为了避免频繁触发搜索请求,可以使用防抖技术。

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Debounce Example</title>
</head>
<body>
  <input type="text" id="search-input" placeholder="Search...">
  <div id="suggestions"></div>

  <script>
    // 防抖函数
    function debounce(func, delay) {
      let timeoutId;
      return function (...args) {
        const context = this;
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
          func.apply(context, args);
        }, delay);
      };
    }

    // 搜索建议函数
    function searchSuggestions(query) {
      console.log(`Searching for: ${query}`);
      const suggestions = document.getElementById('suggestions');
      suggestions.innerHTML = `<p>Showing suggestions for: ${query}</p>`;
    }

    // 获取输入框
    const input = document.getElementById('search-input');

    // 使用防抖绑定输入事件
    input.addEventListener('input', debounce((event) => {
      searchSuggestions(event.target.value);
    }, 500)); // 延迟 500 毫秒
  </script>
</body>
</html>

防抖的高级用法

1. 立即执行与延迟执行

防抖函数可以配置是否在第一次触发时立即执行,而不是延迟执行。以下是改进版的防抖函数:

ini 复制代码
function debounce(func, delay, immediate = false) {
  let timeoutId;
  return function (...args) {
    const context = this;
    const callNow = immediate && !timeoutId; // 是否立即执行
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      if (!immediate) {
        func.apply(context, args); // 延迟执行
      }
      timeoutId = null;
    }, delay);
    if (callNow) {
      func.apply(context, args); // 立即执行
    }
  };
}
使用示例
less 复制代码
// 延迟执行
input.addEventListener('input', debounce((event) => {
  searchSuggestions(event.target.value);
}, 500));

// 立即执行
input.addEventListener('input', debounce((event) => {
  searchSuggestions(event.target.value);
}, 500, true));

与防抖的区别

节流和防抖都是用于限制函数执行频率的技术,但它们的实现和用途有所不同:

  • 节流(Throttle)

    • 在指定的时间间隔内,确保函数只执行一次。
    • 适用于需要定期触发的场景(如滚动、窗口调整大小)。
  • 防抖(Debounce)

    • 在指定的时间间隔内,只有最后一次触发时才执行函数。
    • 适用于需要延迟执行的场景(如输入框的搜索建议、按钮点击)

在 Vue 中使用:

javascript 复制代码
<template>
 <input type="text" v-model="inputValue" @input="debouncedInput" />
</template>

export default {
  data() {
    return {
      inputValue: ""
    };
  },
  methods: {
    handleInput() {
      console.log("Debounced input:", this.inputValue);
    }
  },
  mounted() {
    this.debouncedInput = debounce(this.handleInput, 500);
  }
};

2. 使用 Lodash 的 debounce throttle

Lodash 是一个常用的 JavaScript 工具库,提供了现成的防抖函数

安装 Lodash:
复制代码
npm install lodash
使用 debounce:
javascript 复制代码
import { debounce } from "lodash";

export default {
  data() {
    return {
      inputValue: ""
    };
  },
  methods: {
    handleInput() {
      console.log("Debounced input:", this.inputValue);
    }
  },
  mounted() {
    this.debouncedInput = debounce(this.handleInput, 500);
  }
};

模板绑定:

lua 复制代码
<input type="text" v-model="inputValue" @input="debouncedInput" />
使用 throttle:
xml 复制代码
<template>
  <div>
    <h1>Lodash 节流示例</h1>
    <p>滚动页面查看节流效果</p>
  </div>
</template>

<script>
import { throttle } from 'lodash';

export default {
  name: 'LodashThrottleExample'
  methods: {
    handleScroll() {
      console.log('滚动事件触发');
      // 在这里添加你的滚动逻辑
    }
  },
  mounted() {
    // 使用 Lodash 的 throttle 函数
    this.throttledScroll = throttle(this.handleScroll, 200);
    window.addEventListener('scroll', this.throttledScroll);
  },
  beforeDestroy() {
    window.removeEventListener('scroll', this.throttledScroll);
  }
};
</script>
xml 复制代码
<template>
  <div>
    <h1>Lodash 节流示例</h1>
    <p>滚动页面查看节流效果</p>
  </div>
</template>

<script setup>
import { onMounted, onUnmounted } from 'vue';
import { throttle } from 'lodash';

const handleScroll = () => {
  console.log('滚动事件触发');
  // 在这里添加你的滚动逻辑
};

const throttledScroll = throttle(handleScroll, 200);

onMounted(() => {
  window.addEventListener('scroll', throttledScroll);
});

onUnmounted(() => {
  window.removeEventListener('scroll', throttledScroll);
});
</script>

3. 使用 Vue 组合式 API 封装防抖

Vue 3 的组合式 API 提供了更灵活的方式封装防抖逻辑。

封装 useDebounce
ini 复制代码
import { ref } from "vue";

export function useDebounce(cb, delay = 100) {
  const timer = ref(null);
  const handler = function (...args) {
    clearTimeout(timer.value);
    timer.value = setTimeout(() => {
      cb(...args);
    }, delay);
  };

  const cancel = () => {
    clearTimeout(timer.value);
    timer.value = null;
  };

  return { handler, cancel };
}
使用:
javascript 复制代码
import { useDebounce } from "./useDebounce";

export default {
  setup() {
    const { handler: debouncedInput } = useDebounce(() => {
      console.log("Debounced input");
    }, 500);

    return { debouncedInput };
  }
};

模板绑定:

lua 复制代码
<input type="text" @input="debouncedInput" />

4. 使用 Vue 指令封装防抖

将防抖逻辑封装为 Vue 自定义指令,方便在多个地方复用。

定义指令:
javascript 复制代码
export default {
  inserted(el, binding) {
    const delay = binding.arg || 500;
    let timer;
    el.addEventListener("input", (e) => {
      clearTimeout(timer);
      timer = setTimeout(() => {
        binding.value(e);
      }, delay);
    });
  }
};
使用:
ini 复制代码
<input type="text" v-debounce:500="handleInput" />

5. 使用 VueUse 的 useDebounce

VueUse 是一个基于 Vue 3 的实用工具库,提供了 useDebounce 函数。

安装 VueUse:
bash 复制代码
npm install @vueuse/core
使用:
javascript 复制代码
import { useDebounce } from "@vueuse/core";

export default {
  setup() {
    const inputValue = ref("");
    const debouncedValue = useDebounce(inputValue, 500);

    return { inputValue, debouncedValue };
  }
};

模板绑定:

typescript 复制代码
<input type="text" v-model="inputValue" />
<div>Debounced Value: {{ debouncedValue }}</div>

总结

  • 如果项目中已经使用了 Lodash,可以直接使用 _.debounce
  • 如果需要更灵活的封装,可以使用 Vue 的组合式 API 或自定义指令。
  • 对于 Vue 3 项目,推荐使用 VueUseuseDebounce,它提供了简洁的 API 和良好的性能。
相关推荐
Hsuna17 分钟前
一句配置让你的小程序自动适应Pad端
前端·javascript
pannysp21 分钟前
Vue2 项目安装eslint配置说明
vue.js
渔樵江渚上21 分钟前
玩转图像像素:用 JavaScript 实现酷炫特效和灰度滤镜
前端·javascript·面试
陆康永22 分钟前
uniapp-x vue 特性
javascript·vue.js·uni-app
xuyanzhuqing23 分钟前
Vue3+Vite使用 Puppeteer 代码零侵入进行SEO优化(SSR+Meta)
vue.js·node.js
做测试的小薄1 小时前
当 Selenium 的 click() /send_keys()等方法失效时:JavaScript 在 UI 自动化测试中的神奇用法
javascript·自动化测试·selenium·ui
一 乐3 小时前
招聘信息|基于SprinBoot+vue的招聘信息管理系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·招聘系统
Gazer_S3 小时前
【Auto-Scroll-List 组件设计与实现分析】
前端·javascript·数据结构·vue.js
harry7593 小时前
React 18+ 安全访问浏览器对象终极指南:从原理到生产级解决方案
前端·javascript
Riesenzahn4 小时前
不用第三方库,说说纯js怎么实现读取和导出excel?
前端·javascript