阅读视图

发现新文章,点击刷新页面。

前端性能优化中的技能CD和回城

🚀 前端性能优化必备技巧:深入理解防抖与节流

📚 前言

在前端开发中,性能优化是一个永恒的主题。当我们处理高频触发事件时,如果不进行适当处理,可能会导致以下问题:

  • 🔥 频繁触发事件导致性能下降
  • 💾 不必要的服务器请求
  • 🖥️ 页面卡顿
  • ⚡ 资源浪费

🎯 常见的高频触发场景

  1. 搜索框实时搜索 🔍

    • 用户输入时频繁发起请求
    • 每次按键都触发搜索
  2. 窗口调整事件 📱

    • resize 事件频繁触发
    • 需要重新计算布局
  3. 滚动事件处理 📜

    • scroll 事件持续触发
    • 可能影响页面性能
  4. 按钮提交事件 🖱️

    • 用户重复点击提交
    • 可能导致重复请求

🛡️ 防抖(Debounce)

🎮 生动的游戏类比

想象英雄联盟中的回城机制:

  • 按 B 开始回城,等待 8 秒
  • 受到伤害立即打断,重新计时
  • 必须完整等待才能回城成功

💻 实际应用案例

  1. 搜索建议功能
// 实现搜索框防抖
const searchInput = document.querySelector('#search');
const debouncedSearch = debounce(async (query) => {
  const results = await fetchSearchResults(query);
  updateSearchSuggestions(results);
}, 300);

searchInput.addEventListener('input', (e) => {
  debouncedSearch(e.target.value);
});
  1. 表单验证
// 实现实时表单验证
const emailInput = document.querySelector('#email');
const debouncedValidate = debounce(async (email) => {
  const isValid = await validateEmail(email);
  updateValidationUI(isValid);
}, 500);

emailInput.addEventListener('input', (e) => {
  debouncedValidate(e.target.value);
});

⚡ 节流(Throttle)

🎮 游戏类比

类似英雄联盟技能冷却:

  • 释放技能进入冷却时间
  • 冷却期间无法再次释放
  • 冷却结束才能再次使用

💻 实际应用案例

  1. 无限滚动加载
// 实现滚动加载
const container = document.querySelector('#infinite-list');
const throttledLoad = throttle(async () => {
  if (isNearBottom()) {
    const newItems = await fetchMoreItems();
    appendItems(newItems);
  }
}, 200);

window.addEventListener('scroll', throttledLoad);
  1. 数据统计上报
// 实现用户行为统计
const tracker = throttle((event) => {
  sendAnalytics({
    type: event.type,
    timestamp: Date.now(),
    data: event.data
  });
}, 1000);

document.addEventListener('mousemove', tracker);

🔄 如何选择防抖还是节流?

选择防抖的场景 🛡️

  • ✅ 搜索框输入查询
  • ✅ 表单实时验证
  • ✅ 调整窗口大小
  • ✅ 用户输入校验

选择节流的场景 ⚡

  • ✅ 页面滚动处理
  • ✅ 数据统计上报
  • ✅ 游戏中的按键处理
  • ✅ 射击类游戏的武器发射

💡 性能优化建议

  1. 延迟时间设置 ⏱️

    • 搜索框:300-500ms
    • 表单验证:400-600ms
    • 滚动处理:150-300ms
    • 统计上报:1000-2000ms
  2. 代码优化 📈

    • 使用闭包保存状态
    • 注意内存泄漏
    • 及时清除定时器
    • 考虑是否需要立即执行

📝 总结

选择合适的方案:

  • 防抖:关注最终结果
  • 节流:关注执行频率

🔗 完整实现代码

防抖实现:

// debounce.js
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input style="width: 80%; height: 30px" type="text" id="id" />
    <script>
      let timer = null;
      function debounce(func, delay, immediate = false) {
        return (...args) => {
          //清除定时器  如果注释的话还是那么多频次,只是每次推迟了
          if (timer) {
            clearTimeout(timer);
          }
          if (!timer && immediate) {
            func(...args);
          }
          timer = setTimeout(() => {
            func(...args);
          }, delay);
        };
      }

      function pureFn(...params) {
        console.log('%c执行成功params:', 'color: red; font-size: 14px;', params);
      }

      const dom = document.getElementById('id');
      const debounceFn = debounce(pureFn, 500, true);
      dom.addEventListener('input', e => {
        const value = e.target.value;
        debounceFn(value, Date.now());
      });
    </script>
  </body>
</html>

节流实现:

// throttle.js
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input style="width: 80%; height: 30px" type="text" id="id" />
    <script>
      const throttle = (func, delay) => {
        let last = 0;
        let deferTimer = null;
        return args => {
          // 干掉触发
          let now = Date.now();
          if (last && now < last + delay) {
            clearTimeout(deferTimer);
            deferTimer = setTimeout(() => {
              last = now;
              func(args);
            }, delay);
          } else {
            last = now; // 第一次时间
            func(args); // 先执行一次
          }
        };
      };

      function pureFn(...params) {
        console.log('%c执行成功params:', 'color: red; font-size: 14px;', params);
      }

      const dom = document.getElementById('id');
      const throttleFn = throttle(pureFn, 1000, false);
      dom.addEventListener('input', e => {
        const value = e.target.value;
        throttleFn(value, Date.now());
      });
    </script>
  </body>
</html>

🌟 如果这篇文章对你有帮助,欢迎点赞、收藏和评论!

📢 关注我,一起探讨前端技术!

❌