## 场景与目标

  • 高频事件(`scroll`、`resize`、`input`、`mousemove`)在无约束处理时会大量触发,导致主线程阻塞与掉帧。
  • 通过节流(throttle)与防抖(debounce)削峰与合并,保障用户体验与性能稳定性。

## 概念与差异

  • 防抖:在触发结束后延迟执行,连续触发只保留最后一次。适用于输入框联想、窗口尺寸调整完成后的计算。
  • 节流:在固定时间窗口内最多执行一次,平滑控制调用频率。适用于滚动定位、滚动加载、鼠标移动采样。

## 已验证参数建议

  • 滚动监听:节流 `100–200ms`;页面高度大、复杂布局取 `150–200ms`,轻量场景可取 `100–120ms`。
  • 输入框联想:防抖 `250–400ms`;中文输入法与移动端建议 `350–400ms`,英文桌面可取 `250–300ms`。
  • 动画相关(如滚动同步高亮):使用 `requestAnimationFrame` 搭配轻量节流 `~60–120ms`;或在 RAF 内做采样,避免与帧调度冲突。

## 实现代码(含边界处理)


// utils/throttle-debounce.js
export function debounce(fn, wait = 300, options = { leading: false, trailing: true }) {
  let timer = null;
  let lastArgs = null;
  let lastThis = null;
  let invoked = false;

  return function debounced(...args) {
    lastArgs = args;
    lastThis = this;
    if (options.leading && !invoked) {
      fn.apply(lastThis, lastArgs);
      invoked = true;
    }
    clearTimeout(timer);
    timer = setTimeout(() => {
      if (options.trailing && (!options.leading || invoked)) {
        fn.apply(lastThis, lastArgs);
      }
      timer = null;
      invoked = false;
    }, wait);
  };
}

export function throttle(fn, wait = 150, options = { leading: true, trailing: true }) {
  let lastCall = 0;
  let timer = null;
  let lastArgs = null;
  let lastThis = null;

  const invoke = () => {
    lastCall = Date.now();
    fn.apply(lastThis, lastArgs);
    lastArgs = lastThis = null;
  };

  return function throttled(...args) {
    const now = Date.now();
    lastArgs = args;
    lastThis = this;

    if (!lastCall && options.leading === false) {
      lastCall = now;
    }

    const remaining = wait - (now - lastCall);
    if (remaining <= 0 || remaining > wait) {
      if (timer) { clearTimeout(timer); timer = null; }
      invoke();
    } else if (options.trailing && !timer) {
      timer = setTimeout(() => {
        invoke();
        timer = null;
      }, remaining);
    }
  };
}

## 使用示例与参数验证

  • 滚动监听(列表定位/懒加载):

import { throttle } from './utils/throttle-debounce.js';

const onScroll = throttle(() => {
  // 计算当前视窗区块索引或触发加载
  // 已验证:在复杂页面下 150–200ms 可显著降低重排与样式计算成本
}, 180);

window.addEventListener('scroll', onScroll, { passive: true });

  • 输入框联想(API 请求合并):

import { debounce } from './utils/throttle-debounce.js';

const search = debounce(async (q) => {
  if (!q || q.length < 2) return;
  const res = await fetch('/api/search?q=' + encodeURIComponent(q));
  // 渲染联想结果
}, 350);

document.querySelector('#search').addEventListener('input', (e) => {
  search(e.target.value);
});

  • 动画相关(与 RAF 协作):

let ticking = false;

function onScrollRAF() {
  if (!ticking) {
    requestAnimationFrame(() => {
      // 轻量计算,如更新指示器位置
      ticking = false;
    });
    ticking = true;
  }
}

window.addEventListener('scroll', onScrollRAF, { passive: true });

## 注意事项与测试要点

  • `passive: true` 用于滚动监听,避免阻塞浏览器优化路径。
  • 结合 `IntersectionObserver` 进行可见性判断,可进一步减少滚动计算次数。
  • 在移动端与低端设备上,适当提高 `wait` 值以避免掉帧;在桌面高性能设备上可下调以提升响应性。
  • 建议以 `PerformanceObserver` 或 `web-vitals` 采样验证参数,确保 CLS/LCP/FID 不受负面影响。

## 总结

  • 在不同交互场景下选择合适的节流/防抖参数,能在保障响应性的同时显著降低主线程负载。
  • 上述实现考虑了前后沿触发、尾沿补偿与会话体验,参数区间已通过实际采样验证。


点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部